github.com/coveo/gotemplate@v2.7.7+incompatible/template/slice.go (about) 1 package template 2 3 import ( 4 "fmt" 5 "reflect" 6 7 "github.com/coveo/gotemplate/collections" 8 "github.com/coveo/gotemplate/errors" 9 ) 10 11 func safeIndex(value interface{}, index int, def interface{}) (result interface{}, err error) { 12 defer func() { err = errors.Trap(err, recover()) }() 13 valueOf := reflect.ValueOf(value) 14 switch valueOf.Kind() { 15 case reflect.Slice, reflect.Array, reflect.String: 16 if index < 0 || index >= valueOf.Len() { 17 return def, nil 18 } 19 return valueOf.Index(index).Interface(), nil 20 default: 21 return nil, fmt.Errorf("First argument is not indexable %T", value) 22 } 23 } 24 25 func slice(value interface{}, args ...interface{}) (result interface{}, err error) { 26 return sliceInternal(value, false, args...) 27 } 28 29 func extract(value interface{}, args ...interface{}) (result interface{}, err error) { 30 return sliceInternal(value, true, args...) 31 } 32 33 func sliceInternal(value interface{}, extract bool, args ...interface{}) (result interface{}, err error) { 34 defer func() { err = errors.Trap(err, recover()) }() 35 36 args = convertArgs(nil, args...).AsArray() 37 38 valueOf := reflect.ValueOf(value) 39 switch valueOf.Kind() { 40 case reflect.Slice, reflect.Array, reflect.String: 41 switch len(args) { 42 case 0: 43 return valueOf.Interface(), nil 44 case 1: 45 return selectElement(valueOf, toInt(args[0])), nil 46 case 2: 47 if !extract { 48 return sliceList(valueOf, args...) 49 } 50 fallthrough 51 default: 52 if !extract { 53 return nil, fmt.Errorf("To many parameters") 54 } 55 result := collections.AsList(value).Create(len(args)) 56 for i := range args { 57 result.Set(i, selectElement(valueOf, toInt(args[i]))) 58 } 59 if valueOf.Kind() == reflect.String { 60 return fmt.Sprint(result.AsArray()...), nil 61 } 62 return result, nil 63 } 64 65 case reflect.Map: 66 return sliceMap(value, extract, args...) 67 68 default: 69 return nil, fmt.Errorf("Cannot apply slice on type %s", reflect.TypeOf(value)) 70 } 71 } 72 73 func sliceMap(value interface{}, extract bool, args ...interface{}) (interface{}, error) { 74 dict := collections.AsDictionary(value) 75 switch len(args) { 76 case 0: 77 return nil, nil 78 case 1: 79 return dict.Get(args[0]), nil 80 case 2: 81 if !extract { 82 keys := dict.GetKeys() 83 k1, k2 := fmt.Sprint(args[0]), fmt.Sprint(args[1]) 84 if k1 > k2 { 85 keys = keys.Reverse() 86 k1, k2 = k2, k1 87 } 88 89 results := dict.CreateList(0, dict.Len()*20) 90 for i := 0; i < keys.Len(); i++ { 91 k := fmt.Sprint(keys.Get(i)) 92 if k >= k1 && k <= k2 { 93 results = results.Append(dict.Get(k)) 94 } 95 } 96 return results, nil 97 } 98 fallthrough 99 default: 100 if !extract { 101 return nil, fmt.Errorf("Slice cannot have more that two parts") 102 } 103 results := dict.CreateList(len(args)) 104 for i, key := range args { 105 results.Set(i, dict.Get(key)) 106 } 107 return results, nil 108 } 109 } 110 111 func sliceList(value reflect.Value, args ...interface{}) (result interface{}, err error) { 112 length := value.Len() 113 begin := toInt(args[0]) 114 begin = iif(begin < 0, length+begin+1, begin).(int) 115 end := toInt(args[1]) 116 end = iif(end < 0, length+end+1, end).(int) 117 118 // Check if we should reverse the section 119 reverse := end < begin 120 if reverse { 121 end, begin = begin, end 122 } 123 124 // For slice operation, there is no error if the index are of limit 125 end = int(min(end, length).(int64)) 126 begin = int(max(begin, 0).(int64)) 127 128 if value.Kind() == reflect.String { 129 // String slices are returned as string instead of array of runes 130 result := value.String()[begin:end] 131 if reverse { 132 return reverseString(result), nil 133 } 134 return result, nil 135 } 136 137 if begin > length { 138 // Begin is after the end 139 return collections.AsList(value.Interface()).Create(), nil 140 } 141 results := collections.AsList(value.Interface()).Create(end - begin) 142 for i := range results.AsArray() { 143 results.Set(i, value.Index(i+begin).Interface()) 144 } 145 if reverse { 146 return results.Reverse(), nil 147 } 148 return results, nil 149 } 150 151 func selectElement(value reflect.Value, index int) interface{} { 152 index = iif(index < 0, value.Len()+index, index).(int) 153 if value.Kind() == reflect.String { 154 return value.String()[index : index+1] 155 } 156 return value.Index(index).Interface() 157 } 158 159 func getSingleMapElement(m interface{}) (key, value interface{}, err error) { 160 err = fmt.Errorf("Argument must be a map with a single key") 161 if m == nil { 162 return 163 } 164 t := reflect.TypeOf(m) 165 v := reflect.ValueOf(m) 166 switch t.Kind() { 167 case reflect.Map: 168 keys := v.MapKeys() 169 if len(keys) != 1 { 170 return 171 } 172 return keys[0].Interface(), v.MapIndex(keys[0]).Interface(), nil 173 case reflect.Slice: 174 length := v.Len() 175 keys := make([]interface{}, length) 176 values := make([]interface{}, length) 177 for i := range keys { 178 if keys[i], values[i], err = getSingleMapElement(v.Index(i).Interface()); err != nil { 179 return 180 } 181 } 182 183 results := make(dictionary) 184 for i := range keys { 185 results[fmt.Sprint(keys[i])] = values[i] 186 } 187 return keys, results, nil 188 189 default: 190 return 191 } 192 } 193 194 // Reverse returns its argument string reversed rune-wise left to right. 195 func reverseString(s string) string { 196 r := []rune(s) 197 for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { 198 r[i], r[j] = r[j], r[i] 199 } 200 return string(r) 201 }