...
1 package middleware
2
3 import (
4 "context"
5 "reflect"
6 "strings"
7 )
8
9
10
11
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
20 func ClearStackValues(ctx context.Context) context.Context {
21 return context.WithValue(ctx, stackValuesKey{}, nil)
22 }
23
24
25
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
90
91
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 }
101
View as plain text