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  }