-
Notifications
You must be signed in to change notification settings - Fork 29
/
context.go
156 lines (132 loc) · 4.05 KB
/
context.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package toolbox
import (
"fmt"
"reflect"
"sync"
)
// Context represents type safe map.
type Context interface {
//GetRequired returns a value for a target type of error if it does not exist
GetRequired(targetType interface{}) (interface{}, error)
//GetOptional returns a value for a target type
GetOptional(targetType interface{}) interface{}
//GetOptional into sets requested context value into target, returns true if value was found
GetInto(targetType interface{}, target interface{}) bool
//Put puts target type value to the context, or error if value exists, is nil or incompatible with target type
Put(targetType interface{}, value interface{}) error
//Replace repaces value in the context
Replace(targetType interface{}, value interface{}) error
//Remove removes value from the context
Remove(targetType interface{}) interface{}
//Contains chekcs if a value of a terget type is in contet
Contains(targetType interface{}) bool
//Clone create a shallow copy of a context
Clone() Context
}
type contextImpl struct {
mux sync.RWMutex
context map[string]interface{}
}
func (c *contextImpl) getReflectType(targetType interface{}) reflect.Type {
var reflectType reflect.Type
var ok bool
reflectType, ok = targetType.(reflect.Type)
if !ok {
reflectType = reflect.TypeOf(targetType)
}
return reflectType
}
func (c *contextImpl) getKey(targetType interface{}) string {
var reflectType = c.getReflectType(targetType)
return reflectType.String()
}
func (c *contextImpl) GetRequired(targetType interface{}) (interface{}, error) {
if !c.Contains(targetType) {
key := c.getKey(targetType)
return nil, fmt.Errorf("failed to lookup key:" + key)
}
return c.GetOptional(targetType), nil
}
func (c *contextImpl) GetOptional(targetType interface{}) interface{} {
key := c.getKey(targetType)
c.mux.RLock()
defer c.mux.RUnlock()
if result, ok := c.context[key]; ok {
return result
}
return nil
}
func (c *contextImpl) GetInto(targetType, target interface{}) bool {
key := c.getKey(targetType)
c.mux.RLock()
defer c.mux.RUnlock()
if result, ok := c.context[key]; ok {
reflect.ValueOf(target).Elem().Set(reflect.ValueOf(result))
return true
}
return false
}
func (c *contextImpl) Put(targetType interface{}, value interface{}) error {
if c.Contains(targetType) {
key := c.getKey(targetType)
return fmt.Errorf("failed to put key - already exist: " + key)
}
return c.Replace(targetType, value)
}
func (c *contextImpl) Replace(targetType interface{}, value interface{}) error {
key := c.getKey(targetType)
targetReflectType := c.getReflectType(targetType)
valueReflectType := reflect.TypeOf(value)
if valueReflectType == targetReflectType {
c.mux.Lock()
c.context[key] = value
c.mux.Unlock()
return nil
}
if targetReflectType.Kind() == reflect.Ptr {
converted := reflect.ValueOf(value).Elem().Convert(targetReflectType.Elem())
convertedPointer := reflect.New(targetReflectType.Elem())
convertedPointer.Elem().Set(converted)
value = convertedPointer.Interface()
} else {
if !valueReflectType.AssignableTo(targetReflectType) {
return fmt.Errorf("value of type %v is not assignable to %v", valueReflectType, targetReflectType)
}
value = reflect.ValueOf(value).Convert(targetReflectType).Interface()
}
c.mux.Lock()
c.context[key] = value
c.mux.Unlock()
return nil
}
func (c *contextImpl) Remove(targetType interface{}) interface{} {
key := c.getKey(targetType)
result := c.GetOptional(targetType)
c.mux.Lock()
delete(c.context, key)
c.mux.Unlock()
return result
}
func (c *contextImpl) Contains(targetType interface{}) bool {
key := c.getKey(targetType)
c.mux.RLock()
defer c.mux.RUnlock()
if _, ok := c.context[key]; ok {
return true
}
return false
}
func (c *contextImpl) Clone() Context {
var result = &contextImpl{context: make(map[string]interface{})}
c.mux.RLock()
defer c.mux.RUnlock()
for k, v := range c.context {
result.context[k] = v
}
return result
}
// NewContext creates a new context
func NewContext() Context {
var result Context = &contextImpl{context: make(map[string]interface{})}
return result
}