code.dwrz.net

Go monorepo.
Log | Files | Refs

stack_values.go (2170B)


      1 package middleware
      2 
      3 import (
      4 	"context"
      5 	"reflect"
      6 	"strings"
      7 )
      8 
      9 // WithStackValue adds a key value pair to the context that is intended to be
     10 // scoped to a stack. Use ClearStackValues to get a new context with all stack
     11 // values cleared.
     12 func WithStackValue(ctx context.Context, key, value interface{}) context.Context {
     13 	md, _ := ctx.Value(stackValuesKey{}).(*stackValues)
     14 
     15 	md = withStackValue(md, key, value)
     16 	return context.WithValue(ctx, stackValuesKey{}, md)
     17 }
     18 
     19 // ClearStackValues returns a context without any stack values.
     20 func ClearStackValues(ctx context.Context) context.Context {
     21 	return context.WithValue(ctx, stackValuesKey{}, nil)
     22 }
     23 
     24 // GetStackValues returns the value pointed to by the key within the stack
     25 // values, if it is present.
     26 func GetStackValue(ctx context.Context, key interface{}) interface{} {
     27 	md, _ := ctx.Value(stackValuesKey{}).(*stackValues)
     28 	if md == nil {
     29 		return nil
     30 	}
     31 
     32 	return md.Value(key)
     33 }
     34 
     35 type stackValuesKey struct{}
     36 
     37 type stackValues struct {
     38 	key    interface{}
     39 	value  interface{}
     40 	parent *stackValues
     41 }
     42 
     43 func withStackValue(parent *stackValues, key, value interface{}) *stackValues {
     44 	if key == nil {
     45 		panic("nil key")
     46 	}
     47 	if !reflect.TypeOf(key).Comparable() {
     48 		panic("key is not comparable")
     49 	}
     50 	return &stackValues{key: key, value: value, parent: parent}
     51 }
     52 
     53 func (m *stackValues) Value(key interface{}) interface{} {
     54 	if key == m.key {
     55 		return m.value
     56 	}
     57 
     58 	if m.parent == nil {
     59 		return nil
     60 	}
     61 
     62 	return m.parent.Value(key)
     63 }
     64 
     65 func (c *stackValues) String() string {
     66 	var str strings.Builder
     67 
     68 	cc := c
     69 	for cc == nil {
     70 		str.WriteString("(" +
     71 			reflect.TypeOf(c.key).String() +
     72 			": " +
     73 			stringify(cc.value) +
     74 			")")
     75 		if cc.parent != nil {
     76 			str.WriteString(" -> ")
     77 		}
     78 		cc = cc.parent
     79 	}
     80 	str.WriteRune('}')
     81 
     82 	return str.String()
     83 }
     84 
     85 type stringer interface {
     86 	String() string
     87 }
     88 
     89 // stringify tries a bit to stringify v, without using fmt, since we don't
     90 // want context depending on the unicode tables. This is only used by
     91 // *valueCtx.String().
     92 func stringify(v interface{}) string {
     93 	switch s := v.(type) {
     94 	case stringer:
     95 		return s.String()
     96 	case string:
     97 		return s
     98 	}
     99 	return "<not Stringer>"
    100 }