github.com/coveo/gotemplate@v2.7.7+incompatible/collections/implementation/base_helper.go (about) 1 package implementation 2 3 import ( 4 "fmt" 5 "reflect" 6 7 "github.com/coveo/gotemplate/errors" 8 ) 9 10 type helperBase = BaseHelper 11 type helperList = ListHelper 12 type helperDict = DictHelper 13 14 var must = errors.Must 15 16 // BaseHelper implements basic functionalities required for both IGenericList & IDictionary 17 type BaseHelper struct { 18 ConvertList func(list baseIList) baseIList 19 ConvertDict func(dict baseIDict) baseIDict 20 NeedConversion func(object interface{}, strict bool) bool 21 } 22 23 // AsList converts object to IGenericList object. It panics if conversion is impossible. 24 func (bh BaseHelper) AsList(object interface{}) baseIList { 25 return must(bh.TryAsList(object)).(baseIList) 26 } 27 28 // AsDictionary converts object to IDictionary object. It panics if conversion is impossible. 29 func (bh BaseHelper) AsDictionary(object interface{}) baseIDict { 30 return must(bh.TryAsDictionary(object)).(baseIDict) 31 } 32 33 // Convert tries to convert the supplied object into IDictionary or IGenericList. 34 // Returns the supplied object if not conversion occurred. 35 func (bh BaseHelper) Convert(object interface{}) interface{} { 36 object, _ = bh.TryConvert(object) 37 return object 38 } 39 40 // CreateList creates a new IGenericList with optional size/capacity arguments. 41 func (bh BaseHelper) CreateList(args ...int) baseIList { 42 var size, capacity int 43 switch len(args) { 44 case 0: 45 case 1: 46 size = args[0] 47 case 2: 48 size, capacity = args[0], args[1] 49 default: 50 panic(fmt.Errorf("CreateList only accept 2 arguments, size and capacity")) 51 } 52 if capacity < size { 53 capacity = size 54 } 55 return bh.ConvertList(make(baseList, size, capacity)) 56 } 57 58 // CreateDictionary creates a new IDictionary with optional capacity arguments. 59 func (bh BaseHelper) CreateDictionary(args ...int) baseIDict { 60 var capacity int 61 switch len(args) { 62 case 0: 63 case 1: 64 capacity = args[0] 65 default: 66 panic(fmt.Errorf("CreateList only accept 1 argument for size")) 67 } 68 return bh.ConvertDict(make(baseDict, capacity)) 69 } 70 71 // TryAsDictionary tries to convert any object to IDictionary object. 72 func (bh BaseHelper) TryAsDictionary(object interface{}) (baseIDict, error) { 73 return bh.tryAsDictionary(object, false) 74 } 75 76 // TryAsDictionaryStrict tries to convert any object to IDictionary object. 77 func (bh BaseHelper) TryAsDictionaryStrict(object interface{}) (baseIDict, error) { 78 return bh.tryAsDictionary(object, true) 79 } 80 81 func (bh BaseHelper) tryAsDictionary(object interface{}, strict bool) (baseIDict, error) { 82 if object != nil && reflect.TypeOf(object).Kind() == reflect.Ptr { 83 object = reflect.ValueOf(object).Elem().Interface() 84 } 85 86 var result baseIDict 87 if dict, ok := object.(baseIDict); ok { 88 // The object is already a IDictionary 89 result = dict 90 } else if object == nil { 91 result = bh.CreateDictionary() 92 } else { 93 target := reflect.TypeOf(baseDict{}) 94 objectType := reflect.TypeOf(object) 95 if objectType.ConvertibleTo(target) { 96 result = bh.ConvertDict(reflect.ValueOf(object).Convert(target).Interface().(baseIDict)) 97 } else { 98 switch objectType.Kind() { 99 case reflect.Map: 100 result = bh.CreateDictionary() 101 value := reflect.ValueOf(object) 102 keys := value.MapKeys() 103 for i := range keys { 104 result.Set(fmt.Sprint(keys[i]), value.MapIndex(keys[i]).Interface()) 105 } 106 default: 107 return nil, fmt.Errorf("Object cannot be converted to dictionary: %T", object) 108 } 109 } 110 } 111 112 if bh.NeedConversion(result, strict) { 113 newDict := bh.CreateDictionary() 114 for key, val := range result.AsMap() { 115 // We loop on the key/values to ensure that all values are converted to the 116 // desired type. 117 newDict.Set(key, val) 118 } 119 result = newDict 120 } 121 122 return result, nil 123 } 124 125 // TryAsList tries to convert any object to IGenericList object. 126 func (bh BaseHelper) TryAsList(object interface{}) (baseIList, error) { 127 return bh.tryAsList(object, false) 128 } 129 130 // TryAsListStrict tries to convert any object to IGenericList object. 131 func (bh BaseHelper) TryAsListStrict(object interface{}) (baseIList, error) { 132 return bh.tryAsList(object, true) 133 } 134 135 func (bh BaseHelper) tryAsList(object interface{}, strict bool) (baseIList, error) { 136 if object != nil && reflect.TypeOf(object).Kind() == reflect.Ptr { 137 object = reflect.ValueOf(object).Elem().Interface() 138 } 139 140 var result baseIList 141 if list, ok := object.(baseIList); ok { 142 // The object is already a IGenericList 143 result = list 144 } else if object == nil { 145 result = bh.CreateList() 146 } else { 147 target := reflect.TypeOf(baseList{}) 148 objectType := reflect.TypeOf(object) 149 if objectType.ConvertibleTo(target) { 150 result = bh.ConvertList(reflect.ValueOf(object).Convert(target).Interface().(baseIList)) 151 } else { 152 switch objectType.Kind() { 153 case reflect.Slice, reflect.Array: 154 value := reflect.ValueOf(object) 155 result = bh.CreateList(value.Len()) 156 for i := 0; i < result.Len(); i++ { 157 result.Set(i, value.Index(i).Interface()) 158 } 159 default: 160 return nil, fmt.Errorf("Object cannot be converted to generic list: %T", object) 161 } 162 } 163 } 164 if bh.NeedConversion(result, false) { 165 newList := bh.CreateList(result.Len()) 166 for i, val := range result.AsArray() { 167 newList.Set(i, val) 168 } 169 result = newList 170 } 171 172 return result, nil 173 } 174 175 // TryConvert tries to convert any object to IGenericList or IDictionary object. 176 // Returns true if a conversion occurred. 177 func (bh BaseHelper) TryConvert(object interface{}) (interface{}, bool) { 178 if object != nil { 179 if o, err := bh.TryAsDictionary(object); err == nil { 180 return o, true 181 } else if o, err := bh.TryAsList(object); err == nil { 182 return o, true 183 } 184 } 185 return object, false 186 } 187 188 // NeedConversion determine if the object need deep conversion. 189 // strict indicates that the type must be converted to the desired type 190 // even if the object implements the Dictionary or List interface. 191 func NeedConversion(object interface{}, strict bool, typeName string) bool { 192 if object == nil { 193 return false 194 } 195 objectType := reflect.TypeOf(object) 196 switch objectType.Kind() { 197 case reflect.Map: 198 if dict, ok := object.(baseIDict); !ok || strict && dict.TypeName().Str() != typeName { 199 return true 200 } 201 202 value := reflect.ValueOf(object) 203 keys := value.MapKeys() 204 for i := range keys { 205 if NeedConversion(value.MapIndex(keys[i]).Interface(), strict, typeName) { 206 return true 207 } 208 } 209 case reflect.Slice, reflect.Array: 210 if list, ok := object.(baseIList); !ok || strict && list.TypeName().Str() != typeName { 211 return true 212 } 213 value := reflect.ValueOf(object) 214 for i := 0; i < value.Len(); i++ { 215 if NeedConversion(value.Index(i).Interface(), strict, typeName) { 216 return true 217 } 218 } 219 } 220 return false 221 } 222 223 var needConversionImpl = NeedConversion