Skip to content

Commit

Permalink
middleware: Add utility for stack scoped stack values
Browse files Browse the repository at this point in the history
Adds utilities for values that are intended to be scoped to individual
stacks. Provides utilities for bulk clearing these values as well.

Related to aws/aws-sdk-go-v2#914
  • Loading branch information
jasdel committed Jan 6, 2021
1 parent be219e8 commit 8ad7ff5
Showing 1 changed file with 101 additions and 0 deletions.
101 changes: 101 additions & 0 deletions middleware/stack_values.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package middleware

import (
"context"
"reflect"
"strings"
)

// WithStackValue adds a key value pair to the context that are intended to be
// scoped to a stack. Use ClearStackValues to get a new context with all stack
// values cleared.
func WithStackValue(ctx context.Context, key, value interface{}) context.Context {
md, _ := ctx.Value(stackValuesKey{}).(*stackValues)

md = withStackValue(md, key, value)
return context.WithValue(ctx, stackValuesKey{}, md)
}

// ClearStackValues returns a context without any stack values.
func ClearStackValues(ctx context.Context) context.Context {
return context.WithValue(ctx, stackValuesKey{}, nil)
}

// GetStackValues returns the value pointed to by the key within the stack
// values, if it is present.
func GetStackValue(ctx context.Context, key interface{}) interface{} {
v := ctx.Value(stackValuesKey{})
if v == nil {
return nil
}

md := v.(*stackValues)
return md.Value(key)
}

type stackValuesKey struct{}

type stackValues struct {
key interface{}
value interface{}
parent *stackValues
}

func withStackValue(parent *stackValues, key, value interface{}) *stackValues {
if key == nil {
panic("nil key")
}
if !reflect.TypeOf(key).Comparable() {
panic("key is not comparable")
}
return &stackValues{key: key, value: value, parent: parent}
}

func (m *stackValues) Value(key interface{}) interface{} {
if key == m.key {
return m.value
}

if m.parent == nil {
return nil
}

return m.parent.Value(key)
}

func (c *stackValues) String() string {
var str strings.Builder

cc := c
for cc == nil {
str.WriteString("(" +
reflect.TypeOf(c.key).String() +
": " +
stringify(cc.value) +
")")
if cc.parent != nil {
str.WriteString(" -> ")
}
cc = cc.parent
}
str.WriteRune('}')

return str.String()
}

type stringer interface {
String() string
}

// stringify tries a bit to stringify v, without using fmt, since we don't
// want context depending on the unicode tables. This is only used by
// *valueCtx.String().
func stringify(v interface{}) string {
switch s := v.(type) {
case stringer:
return s.String()
case string:
return s
}
return "<not Stringer>"
}

0 comments on commit 8ad7ff5

Please sign in to comment.