|
|
|
@ -4,6 +4,7 @@ import ( |
|
|
|
|
"context" |
|
|
|
|
"fmt" |
|
|
|
|
"strings" |
|
|
|
|
"sync" |
|
|
|
|
|
|
|
|
|
"github.com/mediocregopher/mediocre-go-lib/mctx" |
|
|
|
|
) |
|
|
|
@ -28,37 +29,76 @@ type child struct { |
|
|
|
|
// A new Component, i.e. the root Component in the hierarchy, can be initialized
|
|
|
|
|
// by doing:
|
|
|
|
|
// new(Component).
|
|
|
|
|
//
|
|
|
|
|
// Method's on Component are thread-safe.
|
|
|
|
|
type Component struct { |
|
|
|
|
l sync.RWMutex |
|
|
|
|
|
|
|
|
|
path []string |
|
|
|
|
parent *Component |
|
|
|
|
children []child |
|
|
|
|
|
|
|
|
|
kv map[interface{}]interface{} |
|
|
|
|
kv map[interface{}]interface{} |
|
|
|
|
ctx context.Context |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// SetValue sets the given key to the given value on the Component, overwriting
|
|
|
|
|
// any previous value for that key.
|
|
|
|
|
func (c *Component) SetValue(key, value interface{}) { |
|
|
|
|
c.l.Lock() |
|
|
|
|
defer c.l.Unlock() |
|
|
|
|
if c.kv == nil { |
|
|
|
|
c.kv = make(map[interface{}]interface{}, 1) |
|
|
|
|
} |
|
|
|
|
c.kv[key] = value |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (c *Component) value(key interface{}) (interface{}, bool) { |
|
|
|
|
c.l.RLock() |
|
|
|
|
defer c.l.RUnlock() |
|
|
|
|
if c.kv == nil { |
|
|
|
|
return nil, false |
|
|
|
|
} |
|
|
|
|
value, ok := c.kv[key] |
|
|
|
|
return value, ok |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Value returns the value which has been set for the given key.
|
|
|
|
|
func (c *Component) Value(key interface{}) interface{} { |
|
|
|
|
return c.kv[key] |
|
|
|
|
value, _ := c.value(key) |
|
|
|
|
return value |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Values returns all key/value pairs which have been set via SetValue. The
|
|
|
|
|
// returned map should _not_ be modified.
|
|
|
|
|
// InheritedValue returns the value which has been set for the given key. It first
|
|
|
|
|
// looks for the key on the receiver Component. If not found, it will look on
|
|
|
|
|
// its parent Component, and so on, until the key is found. If the key is not
|
|
|
|
|
// found on the root Component then false is returned.
|
|
|
|
|
func (c *Component) InheritedValue(key interface{}) (interface{}, bool) { |
|
|
|
|
value, ok := c.value(key) |
|
|
|
|
if ok { |
|
|
|
|
return value, ok |
|
|
|
|
} else if c.parent == nil { |
|
|
|
|
return nil, false |
|
|
|
|
} |
|
|
|
|
return c.parent.InheritedValue(key) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Values returns all key/value pairs which have been set via SetValue.
|
|
|
|
|
func (c *Component) Values() map[interface{}]interface{} { |
|
|
|
|
return c.kv |
|
|
|
|
c.l.RLock() |
|
|
|
|
defer c.l.RUnlock() |
|
|
|
|
out := make(map[interface{}]interface{}, len(c.kv)) |
|
|
|
|
for k, v := range c.kv { |
|
|
|
|
out[k] = v |
|
|
|
|
} |
|
|
|
|
return out |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// HasValue returns true if the given key has had a value set on it with
|
|
|
|
|
// SetValue.
|
|
|
|
|
func (c *Component) HasValue(key interface{}) bool { |
|
|
|
|
c.l.RLock() |
|
|
|
|
defer c.l.RUnlock() |
|
|
|
|
_, ok := c.kv[key] |
|
|
|
|
return ok |
|
|
|
|
} |
|
|
|
@ -69,6 +109,8 @@ func (c *Component) HasValue(key interface{}) bool { |
|
|
|
|
//
|
|
|
|
|
// If a child of the given name has already been created this method will panic.
|
|
|
|
|
func (c *Component) Child(name string) *Component { |
|
|
|
|
c.l.Lock() |
|
|
|
|
defer c.l.Unlock() |
|
|
|
|
for _, child := range c.children { |
|
|
|
|
if child.name == name { |
|
|
|
|
panic(fmt.Sprintf("child with name %q already exists", name)) |
|
|
|
@ -86,6 +128,8 @@ func (c *Component) Child(name string) *Component { |
|
|
|
|
// Children returns all Components created via the Child method on this
|
|
|
|
|
// Component, in the order they were created.
|
|
|
|
|
func (c *Component) Children() []*Component { |
|
|
|
|
c.l.RLock() |
|
|
|
|
defer c.l.RUnlock() |
|
|
|
|
children := make([]*Component, len(c.children)) |
|
|
|
|
for i := range c.children { |
|
|
|
|
children[i] = c.children[i].Component |
|
|
|
@ -97,6 +141,8 @@ func (c *Component) Children() []*Component { |
|
|
|
|
// or false if this Component was not created via Child (and is therefore the
|
|
|
|
|
// root Component).
|
|
|
|
|
func (c *Component) Name() (string, bool) { |
|
|
|
|
c.l.RLock() |
|
|
|
|
defer c.l.RUnlock() |
|
|
|
|
if len(c.path) == 0 { |
|
|
|
|
return "", false |
|
|
|
|
} |
|
|
|
@ -115,11 +161,14 @@ func (c *Component) Name() (string, bool) { |
|
|
|
|
// fmt.Printf("%#v\n", grandChild.Path()) // []string{"child", "grandchild"}
|
|
|
|
|
//
|
|
|
|
|
func (c *Component) Path() []string { |
|
|
|
|
c.l.RLock() |
|
|
|
|
defer c.l.RUnlock() |
|
|
|
|
return c.path |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (c *Component) pathStr() string { |
|
|
|
|
path := c.Path() |
|
|
|
|
path := make([]string, len(c.path)) |
|
|
|
|
copy(path, c.path) |
|
|
|
|
for i := range path { |
|
|
|
|
path[i] = strings.ReplaceAll(path[i], "/", `\/`) |
|
|
|
|
} |
|
|
|
@ -128,14 +177,28 @@ func (c *Component) pathStr() string { |
|
|
|
|
|
|
|
|
|
type annotateKey string |
|
|
|
|
|
|
|
|
|
// Annotate annotates the given Context with information about the Component.
|
|
|
|
|
func (c *Component) Annotate(ctx context.Context) context.Context { |
|
|
|
|
return mctx.Annotate(ctx, annotateKey("componentPath"), c.pathStr()) |
|
|
|
|
func (c *Component) getCtx() context.Context { |
|
|
|
|
if c.ctx == nil { |
|
|
|
|
c.ctx = mctx.Annotated(annotateKey("componentPath"), c.pathStr()) |
|
|
|
|
} |
|
|
|
|
return c.ctx |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Annotate annotates the Component's internal Context in-place, such that they
|
|
|
|
|
// will be included in any future calls to the Context method.
|
|
|
|
|
func (c *Component) Annotate(kv ...interface{}) { |
|
|
|
|
c.l.Lock() |
|
|
|
|
defer c.l.Unlock() |
|
|
|
|
c.ctx = mctx.Annotate(c.getCtx(), kv...) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Annotated is a shortcut for `c.Annotate(context.Background())`.
|
|
|
|
|
func (c *Component) Annotated() context.Context { |
|
|
|
|
return c.Annotate(context.Background()) |
|
|
|
|
// Context returns a Context which has been annotated with any annotations from
|
|
|
|
|
// Annotate calls to this Component, as well as some default annotations which
|
|
|
|
|
// are always included.
|
|
|
|
|
func (c *Component) Context() context.Context { |
|
|
|
|
c.l.Lock() |
|
|
|
|
defer c.l.Unlock() |
|
|
|
|
return c.getCtx() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// BreadthFirstVisit visits this Component and all of its children, and their
|
|
|
|
|