github.com/shohhei1126/hugo@v0.42.2-0.20180623210752-3d5928889ad7/tpl/collections/apply.go (about) 1 // Copyright 2017 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package collections 15 16 import ( 17 "errors" 18 "fmt" 19 "reflect" 20 "strings" 21 22 "github.com/gohugoio/hugo/tpl" 23 ) 24 25 // Apply takes a map, array, or slice and returns a new slice with the function fname applied over it. 26 func (ns *Namespace) Apply(seq interface{}, fname string, args ...interface{}) (interface{}, error) { 27 if seq == nil { 28 return make([]interface{}, 0), nil 29 } 30 31 if fname == "apply" { 32 return nil, errors.New("can't apply myself (no turtles allowed)") 33 } 34 35 seqv := reflect.ValueOf(seq) 36 seqv, isNil := indirect(seqv) 37 if isNil { 38 return nil, errors.New("can't iterate over a nil value") 39 } 40 41 fnv, found := ns.lookupFunc(fname) 42 if !found { 43 return nil, errors.New("can't find function " + fname) 44 } 45 46 // fnv := reflect.ValueOf(fn) 47 48 switch seqv.Kind() { 49 case reflect.Array, reflect.Slice: 50 r := make([]interface{}, seqv.Len()) 51 for i := 0; i < seqv.Len(); i++ { 52 vv := seqv.Index(i) 53 54 vvv, err := applyFnToThis(fnv, vv, args...) 55 56 if err != nil { 57 return nil, err 58 } 59 60 r[i] = vvv.Interface() 61 } 62 63 return r, nil 64 default: 65 return nil, fmt.Errorf("can't apply over %v", seq) 66 } 67 } 68 69 func applyFnToThis(fn, this reflect.Value, args ...interface{}) (reflect.Value, error) { 70 n := make([]reflect.Value, len(args)) 71 for i, arg := range args { 72 if arg == "." { 73 n[i] = this 74 } else { 75 n[i] = reflect.ValueOf(arg) 76 } 77 } 78 79 num := fn.Type().NumIn() 80 81 if fn.Type().IsVariadic() { 82 num-- 83 } 84 85 // TODO(bep) see #1098 - also see template_tests.go 86 /*if len(args) < num { 87 return reflect.ValueOf(nil), errors.New("Too few arguments") 88 } else if len(args) > num { 89 return reflect.ValueOf(nil), errors.New("Too many arguments") 90 }*/ 91 92 for i := 0; i < num; i++ { 93 // AssignableTo reports whether xt is assignable to type targ. 94 if xt, targ := n[i].Type(), fn.Type().In(i); !xt.AssignableTo(targ) { 95 return reflect.ValueOf(nil), errors.New("called apply using " + xt.String() + " as type " + targ.String()) 96 } 97 } 98 99 res := fn.Call(n) 100 101 if len(res) == 1 || res[1].IsNil() { 102 return res[0], nil 103 } 104 return reflect.ValueOf(nil), res[1].Interface().(error) 105 } 106 107 func (ns *Namespace) lookupFunc(fname string) (reflect.Value, bool) { 108 if !strings.ContainsRune(fname, '.') { 109 templ, ok := ns.deps.Tmpl.(tpl.TemplateFuncsGetter) 110 if !ok { 111 panic("Needs a tpl.TemplateFuncsGetter") 112 } 113 fm := templ.GetFuncs() 114 fn, found := fm[fname] 115 if !found { 116 return reflect.Value{}, false 117 } 118 119 return reflect.ValueOf(fn), true 120 } 121 122 ss := strings.SplitN(fname, ".", 2) 123 124 // namespace 125 nv, found := ns.lookupFunc(ss[0]) 126 if !found { 127 return reflect.Value{}, false 128 } 129 130 // method 131 m := nv.MethodByName(ss[1]) 132 // if reflect.DeepEqual(m, reflect.Value{}) { 133 if m.Kind() == reflect.Invalid { 134 return reflect.Value{}, false 135 } 136 return m, true 137 } 138 139 // indirect is taken from 'text/template/exec.go' 140 func indirect(v reflect.Value) (rv reflect.Value, isNil bool) { 141 for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() { 142 if v.IsNil() { 143 return v, true 144 } 145 if v.Kind() == reflect.Interface && v.NumMethod() > 0 { 146 break 147 } 148 } 149 return v, false 150 } 151 152 func indirectInterface(v reflect.Value) (rv reflect.Value, isNil bool) { 153 for ; v.Kind() == reflect.Interface; v = v.Elem() { 154 if v.IsNil() { 155 return v, true 156 } 157 if v.Kind() == reflect.Interface && v.NumMethod() > 0 { 158 break 159 } 160 } 161 return v, false 162 }