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  }