diff options
author | digitalcraftsman <digitalcraftsman@users.noreply.github.com> | 2017-03-12 23:04:12 +0100 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2017-03-12 23:04:12 +0100 |
commit | 5d0748ce51d0a86843bfd6569fd3cd18fa20ed5a (patch) | |
tree | 0f701a86fc3da58effb2ea2d6c4d14fbcb5f9d11 /tpl | |
parent | 63e2a46f630ea95b6a82ddd7af240f87026871fb (diff) |
tpl: Add union template func
Diffstat (limited to 'tpl')
-rw-r--r-- | tpl/tplimpl/template_funcs.go | 50 | ||||
-rw-r--r-- | tpl/tplimpl/template_funcs_test.go | 51 |
2 files changed, 99 insertions, 2 deletions
diff --git a/tpl/tplimpl/template_funcs.go b/tpl/tplimpl/template_funcs.go index 05382825b..987a00d98 100644 --- a/tpl/tplimpl/template_funcs.go +++ b/tpl/tplimpl/template_funcs.go @@ -399,6 +399,55 @@ func intersect(l1, l2 interface{}) (interface{}, error) { } } +// union returns the union of the given sets, l1 and l2. l1 and +// l2 must be of the same type and may be either arrays or slices. +// If l1 and l2 aren't of the same type then l1 will be returned. +// If either l1 or l2 is nil then the non-nil list will be returned. +func union(l1, l2 interface{}) (interface{}, error) { + if l1 == nil && l2 == nil { + return nil, errors.New("both arrays/slices have to be of the same type") + } else if l1 == nil && l2 != nil { + return l2, nil + } else if l1 != nil && l2 == nil { + return l1, nil + } + + l1v := reflect.ValueOf(l1) + l2v := reflect.ValueOf(l2) + + switch l1v.Kind() { + case reflect.Array, reflect.Slice: + switch l2v.Kind() { + case reflect.Array, reflect.Slice: + r := reflect.MakeSlice(l1v.Type(), 0, 0) + + if l1v.Type() != l2v.Type() { + return r.Interface(), nil + } + + for i := 0; i < l1v.Len(); i++ { + elem := l1v.Index(i) + if !in(r.Interface(), elem.Interface()) { + r = reflect.Append(r, elem) + } + } + + for j := 0; j < l2v.Len(); j++ { + elem := l2v.Index(j) + if !in(r.Interface(), elem.Interface()) { + r = reflect.Append(r, elem) + } + } + + return r.Interface(), nil + default: + return nil, errors.New("can't iterate over " + reflect.ValueOf(l2).Type().String()) + } + default: + return nil, errors.New("can't iterate over " + reflect.ValueOf(l1).Type().String()) + } +} + type imageHandler struct { imageConfigCache map[string]image.Config sync.RWMutex @@ -2193,6 +2242,7 @@ func (t *templateFuncster) initFuncMap() { "time": asTime, "trim": trim, "truncate": truncate, + "union": union, "upper": upper, "urlize": t.PathSpec.URLize, "where": where, diff --git a/tpl/tplimpl/template_funcs_test.go b/tpl/tplimpl/template_funcs_test.go index 491eac56c..355de182f 100644 --- a/tpl/tplimpl/template_funcs_test.go +++ b/tpl/tplimpl/template_funcs_test.go @@ -184,6 +184,7 @@ trim: {{ trim "++Batman--" "+-" }} truncate: {{ "this is a very long text" | truncate 10 " ..." }} truncate: {{ "With [Markdown](/markdown) inside." | markdownify | truncate 14 }} upper: {{upper "BatMan"}} +union: {{ union (slice 1 2 3) (slice 3 4 5) }} urlize: {{ "Bat Man" | urlize }} ` @@ -260,6 +261,7 @@ trim: Batman truncate: this is a ... truncate: With <a href="/markdown">Markdown …</a> upper: BATMAN +union: [1 2 3 4 5] urlize: bat-man ` @@ -988,8 +990,6 @@ func TestIntersect(t *testing.T) { {[]string{"a", "b"}, []string{"a", "b", "c"}, []string{"a", "b"}}, {[]string{"a", "b", "c"}, []string{"d", "e"}, []string{}}, {[]string{}, []string{}, []string{}}, - {[]string{"a", "b"}, nil, make([]interface{}, 0)}, - {nil, []string{"a", "b"}, make([]interface{}, 0)}, {nil, nil, make([]interface{}, 0)}, {[]string{"1", "2"}, []int{1, 2}, []string{}}, {[]int{1, 2}, []string{"1", "2"}, []int{}}, @@ -1021,6 +1021,53 @@ func TestIntersect(t *testing.T) { } } +func TestUnion(t *testing.T) { + t.Parallel() + for i, this := range []struct { + sequence1 interface{} + sequence2 interface{} + expect interface{} + isErr bool + }{ + {[]string{"a", "b", "c", "c"}, []string{"a", "b", "b"}, []string{"a", "b", "c"}, false}, + {[]string{"a", "b"}, []string{"a", "b", "c"}, []string{"a", "b", "c"}, false}, + {[]string{"a", "b", "c"}, []string{"d", "e"}, []string{"a", "b", "c", "d", "e"}, false}, + {[]string{}, []string{}, []string{}, false}, + {[]string{"a", "b"}, nil, []string{"a", "b"}, false}, + {nil, []string{"a", "b"}, []string{"a", "b"}, false}, + {nil, nil, make([]interface{}, 0), true}, + {[]string{"1", "2"}, []int{1, 2}, make([]string, 0), false}, + {[]int{1, 2}, []string{"1", "2"}, make([]int, 0), false}, + {[]int{1, 2, 3}, []int{3, 4, 5}, []int{1, 2, 3, 4, 5}, false}, + {[]int{1, 2, 3}, []int{1, 2, 3}, []int{1, 2, 3}, false}, + {[]int{1, 2, 4}, []int{2, 4}, []int{1, 2, 4}, false}, + {[]int{2, 4}, []int{1, 2, 4}, []int{2, 4, 1}, false}, + {[]int{1, 2, 4}, []int{3, 6}, []int{1, 2, 4, 3, 6}, false}, + {[]float64{2.2, 4.4}, []float64{1.1, 2.2, 4.4}, []float64{2.2, 4.4, 1.1}, false}, + } { + results, err := union(this.sequence1, this.sequence2) + if err != nil && !this.isErr { + t.Errorf("[%d] failed: %s", i, err) + continue + } + if !reflect.DeepEqual(results, this.expect) && !this.isErr { + t.Errorf("[%d] got %v but expected %v", i, results, this.expect) + } + } + + _, err1 := union("not an array or slice", []string{"a"}) + + if err1 == nil { + t.Error("Expected error for non array as first arg") + } + + _, err2 := union([]string{"a"}, "not an array or slice") + + if err2 == nil { + t.Error("Expected error for non array as second arg") + } +} + func TestIsSet(t *testing.T) { t.Parallel() |