mctx: implement beginnings of Annotations functionality
This commit is contained in:
parent
4b446a0efc
commit
000a57689d
71
mctx/annotate.go
Normal file
71
mctx/annotate.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package mctx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type annotateKey struct {
|
||||||
|
userKey interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Annotate takes in one or more key/value pairs (kvs' length must be even) and
|
||||||
|
// returns a Context carrying them. Annotations only exist on the local level,
|
||||||
|
// i.e. a child and parent share different annotation namespaces.
|
||||||
|
func Annotate(ctx context.Context, kvs ...interface{}) context.Context {
|
||||||
|
for i := 0; i < len(kvs); i += 2 {
|
||||||
|
ctx = WithLocalValue(ctx, annotateKey{kvs[i]}, kvs[i+1])
|
||||||
|
}
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
// Annotations describes a set of keys/values which were set on a Context (but
|
||||||
|
// not its parents or children) using Annotate.
|
||||||
|
type Annotations [][2]interface{}
|
||||||
|
|
||||||
|
// LocalAnnotations returns all key/value pairs which have been set via Annotate
|
||||||
|
// on this Context (but not its parent or children). If a key was set twice then
|
||||||
|
// only the most recent value is included. The returned order is
|
||||||
|
// non-deterministic.
|
||||||
|
func LocalAnnotations(ctx context.Context) Annotations {
|
||||||
|
var annotations Annotations
|
||||||
|
localValuesIter(ctx, func(key, val interface{}) {
|
||||||
|
aKey, ok := key.(annotateKey)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
annotations = append(annotations, [2]interface{}{aKey.userKey, val})
|
||||||
|
})
|
||||||
|
return annotations
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringMap formats each of the key/value pairs into strings using fmt.Sprint.
|
||||||
|
// If any two keys format to the same string, then type information will be
|
||||||
|
// prefaced to each one.
|
||||||
|
func (aa Annotations) StringMap() map[string]string {
|
||||||
|
keyTypes := make(map[string]interface{}, len(aa))
|
||||||
|
keyVals := map[string]string{}
|
||||||
|
setKey := func(k, v interface{}) {
|
||||||
|
kStr := fmt.Sprint(k)
|
||||||
|
oldType := keyTypes[kStr]
|
||||||
|
if oldType == nil {
|
||||||
|
keyTypes[kStr] = k
|
||||||
|
keyVals[kStr] = fmt.Sprint(v)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if oldKey is in kvm, if so it needs to be moved to account for
|
||||||
|
// its type info
|
||||||
|
if oldV, ok := keyVals[kStr]; ok {
|
||||||
|
delete(keyVals, kStr)
|
||||||
|
keyVals[fmt.Sprintf("%T(%s)", oldType, kStr)] = oldV
|
||||||
|
}
|
||||||
|
|
||||||
|
keyVals[fmt.Sprintf("%T(%s)", k, kStr)] = fmt.Sprint(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, kv := range aa {
|
||||||
|
setKey(kv[0], kv[1])
|
||||||
|
}
|
||||||
|
return keyVals
|
||||||
|
}
|
55
mctx/annotate_test.go
Normal file
55
mctx/annotate_test.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package mctx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
. "testing"
|
||||||
|
|
||||||
|
"github.com/mediocregopher/mediocre-go-lib/mtest/massert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAnnotate(t *T) {
|
||||||
|
parent := context.Background()
|
||||||
|
parent = Annotate(parent, "a", "foo")
|
||||||
|
parent = Annotate(parent, "b", "bar")
|
||||||
|
|
||||||
|
child := NewChild(parent, "child")
|
||||||
|
child = Annotate(child, "a", "FOO")
|
||||||
|
child = Annotate(child, "c", "BAZ")
|
||||||
|
parent = WithChild(parent, child)
|
||||||
|
|
||||||
|
parentAnnotations := LocalAnnotations(parent)
|
||||||
|
childAnnotations := LocalAnnotations(child)
|
||||||
|
massert.Fatal(t, massert.All(
|
||||||
|
massert.Len(parentAnnotations, 2),
|
||||||
|
massert.Has(parentAnnotations, [2]interface{}{"a", "foo"}),
|
||||||
|
massert.Has(parentAnnotations, [2]interface{}{"b", "bar"}),
|
||||||
|
massert.Len(childAnnotations, 2),
|
||||||
|
massert.Has(childAnnotations, [2]interface{}{"a", "FOO"}),
|
||||||
|
massert.Has(childAnnotations, [2]interface{}{"c", "BAZ"}),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnnotationsStingMap(t *T) {
|
||||||
|
type A int
|
||||||
|
type B int
|
||||||
|
aa := Annotations{
|
||||||
|
{"foo", "bar"},
|
||||||
|
{"1", "one"},
|
||||||
|
{1, 1},
|
||||||
|
{0, 0},
|
||||||
|
{A(0), 0},
|
||||||
|
{B(0), 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := massert.Equal(map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
"string(1)": "one",
|
||||||
|
"int(1)": "1",
|
||||||
|
"int(0)": "0",
|
||||||
|
"mctx.A(0)": "0",
|
||||||
|
"mctx.B(0)": "0",
|
||||||
|
}, aa.StringMap()).Assert()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
21
mctx/ctx.go
21
mctx/ctx.go
@ -218,17 +218,26 @@ func LocalValue(ctx context.Context, key interface{}) interface{} {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalValues returns all key/value pairs which have been set on the Context
|
func localValuesIter(ctx context.Context, callback func(key, val interface{})) {
|
||||||
// via WithLocalValue.
|
m := map[interface{}]struct{}{}
|
||||||
func LocalValues(ctx context.Context) map[interface{}]interface{} {
|
|
||||||
m := map[interface{}]interface{}{}
|
|
||||||
lv, _ := ctx.Value(localValsKey(0)).(*localVal)
|
lv, _ := ctx.Value(localValsKey(0)).(*localVal)
|
||||||
for {
|
for {
|
||||||
if lv == nil {
|
if lv == nil {
|
||||||
return m
|
return
|
||||||
} else if _, ok := m[lv.key]; !ok {
|
} else if _, ok := m[lv.key]; !ok {
|
||||||
m[lv.key] = lv.val
|
callback(lv.key, lv.val)
|
||||||
|
m[lv.key] = struct{}{}
|
||||||
}
|
}
|
||||||
lv = lv.prev
|
lv = lv.prev
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LocalValues returns all key/value pairs which have been set on the Context
|
||||||
|
// via WithLocalValue.
|
||||||
|
func LocalValues(ctx context.Context) map[interface{}]interface{} {
|
||||||
|
m := map[interface{}]interface{}{}
|
||||||
|
localValuesIter(ctx, func(key, val interface{}) {
|
||||||
|
m[key] = val
|
||||||
|
})
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user