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