github.com/graemephi/kahugo@v0.62.3-0.20211121071557-d78c0423784d/tpl/tplimpl/template_funcs.go (about) 1 // Copyright 2017-present The Hugo Authors. All rights reserved. 2 // 3 // Portions Copyright The Go Authors. 4 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 package tplimpl 17 18 import ( 19 "reflect" 20 "strings" 21 22 "github.com/gohugoio/hugo/tpl" 23 24 "github.com/gohugoio/hugo/common/maps" 25 26 template "github.com/gohugoio/hugo/tpl/internal/go_templates/htmltemplate" 27 texttemplate "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate" 28 29 "github.com/gohugoio/hugo/deps" 30 31 "github.com/gohugoio/hugo/tpl/internal" 32 33 // Init the namespaces 34 _ "github.com/gohugoio/hugo/tpl/cast" 35 _ "github.com/gohugoio/hugo/tpl/collections" 36 _ "github.com/gohugoio/hugo/tpl/compare" 37 _ "github.com/gohugoio/hugo/tpl/crypto" 38 _ "github.com/gohugoio/hugo/tpl/data" 39 _ "github.com/gohugoio/hugo/tpl/debug" 40 _ "github.com/gohugoio/hugo/tpl/encoding" 41 _ "github.com/gohugoio/hugo/tpl/fmt" 42 _ "github.com/gohugoio/hugo/tpl/hugo" 43 _ "github.com/gohugoio/hugo/tpl/images" 44 _ "github.com/gohugoio/hugo/tpl/inflect" 45 _ "github.com/gohugoio/hugo/tpl/js" 46 _ "github.com/gohugoio/hugo/tpl/lang" 47 _ "github.com/gohugoio/hugo/tpl/math" 48 _ "github.com/gohugoio/hugo/tpl/openapi/openapi3" 49 _ "github.com/gohugoio/hugo/tpl/os" 50 _ "github.com/gohugoio/hugo/tpl/partials" 51 _ "github.com/gohugoio/hugo/tpl/path" 52 _ "github.com/gohugoio/hugo/tpl/reflect" 53 _ "github.com/gohugoio/hugo/tpl/resources" 54 _ "github.com/gohugoio/hugo/tpl/safe" 55 _ "github.com/gohugoio/hugo/tpl/site" 56 _ "github.com/gohugoio/hugo/tpl/strings" 57 _ "github.com/gohugoio/hugo/tpl/templates" 58 _ "github.com/gohugoio/hugo/tpl/time" 59 _ "github.com/gohugoio/hugo/tpl/transform" 60 _ "github.com/gohugoio/hugo/tpl/urls" 61 ) 62 63 var ( 64 _ texttemplate.ExecHelper = (*templateExecHelper)(nil) 65 zero reflect.Value 66 ) 67 68 type templateExecHelper struct { 69 running bool // whether we're in server mode. 70 funcs map[string]reflect.Value 71 } 72 73 func (t *templateExecHelper) GetFunc(tmpl texttemplate.Preparer, name string) (reflect.Value, bool) { 74 if fn, found := t.funcs[name]; found { 75 return fn, true 76 } 77 return zero, false 78 } 79 80 func (t *templateExecHelper) GetMapValue(tmpl texttemplate.Preparer, receiver, key reflect.Value) (reflect.Value, bool) { 81 if params, ok := receiver.Interface().(maps.Params); ok { 82 // Case insensitive. 83 keystr := strings.ToLower(key.String()) 84 v, found := params[keystr] 85 if !found { 86 return zero, false 87 } 88 return reflect.ValueOf(v), true 89 } 90 91 v := receiver.MapIndex(key) 92 93 return v, v.IsValid() 94 } 95 96 func (t *templateExecHelper) GetMethod(tmpl texttemplate.Preparer, receiver reflect.Value, name string) (method reflect.Value, firstArg reflect.Value) { 97 if t.running { 98 // This is a hot path and receiver.MethodByName really shows up in the benchmarks, 99 // so we maintain a list of method names with that signature. 100 switch name { 101 case "GetPage", "Render": 102 if info, ok := tmpl.(tpl.Info); ok { 103 if m := receiver.MethodByName(name + "WithTemplateInfo"); m.IsValid() { 104 return m, reflect.ValueOf(info) 105 } 106 } 107 } 108 } 109 110 return receiver.MethodByName(name), zero 111 } 112 113 func newTemplateExecuter(d *deps.Deps) (texttemplate.Executer, map[string]reflect.Value) { 114 funcs := createFuncMap(d) 115 funcsv := make(map[string]reflect.Value) 116 117 for k, v := range funcs { 118 vv := reflect.ValueOf(v) 119 funcsv[k] = vv 120 } 121 122 // Duplicate Go's internal funcs here for faster lookups. 123 for k, v := range template.GoFuncs { 124 if _, exists := funcsv[k]; !exists { 125 vv, ok := v.(reflect.Value) 126 if !ok { 127 vv = reflect.ValueOf(v) 128 } 129 funcsv[k] = vv 130 } 131 } 132 133 for k, v := range texttemplate.GoFuncs { 134 if _, exists := funcsv[k]; !exists { 135 funcsv[k] = v 136 } 137 } 138 139 exeHelper := &templateExecHelper{ 140 running: d.Running, 141 funcs: funcsv, 142 } 143 144 return texttemplate.NewExecuter( 145 exeHelper, 146 ), funcsv 147 } 148 149 func createFuncMap(d *deps.Deps) map[string]interface{} { 150 funcMap := template.FuncMap{} 151 152 // Merge the namespace funcs 153 for _, nsf := range internal.TemplateFuncsNamespaceRegistry { 154 ns := nsf(d) 155 if _, exists := funcMap[ns.Name]; exists { 156 panic(ns.Name + " is a duplicate template func") 157 } 158 funcMap[ns.Name] = ns.Context 159 for _, mm := range ns.MethodMappings { 160 for _, alias := range mm.Aliases { 161 if _, exists := funcMap[alias]; exists { 162 panic(alias + " is a duplicate template func") 163 } 164 funcMap[alias] = mm.Method 165 } 166 } 167 } 168 169 if d.OverloadedTemplateFuncs != nil { 170 for k, v := range d.OverloadedTemplateFuncs { 171 funcMap[k] = v 172 } 173 } 174 175 return funcMap 176 }