github.com/graemephi/kahugo@v0.62.3-0.20211121071557-d78c0423784d/tpl/internal/go_templates/texttemplate/hugo_template.go (about)

     1  // Copyright 2019 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 template
    15  
    16  import (
    17  	"io"
    18  	"reflect"
    19  
    20  	"github.com/gohugoio/hugo/common/hreflect"
    21  
    22  	"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse"
    23  )
    24  
    25  /*
    26  
    27  This files contains the Hugo related addons. All the other files in this
    28  package is auto generated.
    29  
    30  */
    31  
    32  // Export it so we can populate Hugo's func map with it, which makes it faster.
    33  var GoFuncs = builtinFuncs()
    34  
    35  // Preparer prepares the template before execution.
    36  type Preparer interface {
    37  	Prepare() (*Template, error)
    38  }
    39  
    40  // ExecHelper allows some custom eval hooks.
    41  type ExecHelper interface {
    42  	GetFunc(tmpl Preparer, name string) (reflect.Value, bool)
    43  	GetMethod(tmpl Preparer, receiver reflect.Value, name string) (method reflect.Value, firstArg reflect.Value)
    44  	GetMapValue(tmpl Preparer, receiver, key reflect.Value) (reflect.Value, bool)
    45  }
    46  
    47  // Executer executes a given template.
    48  type Executer interface {
    49  	Execute(p Preparer, wr io.Writer, data interface{}) error
    50  }
    51  
    52  type executer struct {
    53  	helper ExecHelper
    54  }
    55  
    56  func NewExecuter(helper ExecHelper) Executer {
    57  	return &executer{helper: helper}
    58  }
    59  
    60  func (t *executer) Execute(p Preparer, wr io.Writer, data interface{}) error {
    61  	tmpl, err := p.Prepare()
    62  	if err != nil {
    63  		return err
    64  	}
    65  
    66  	value, ok := data.(reflect.Value)
    67  	if !ok {
    68  		value = reflect.ValueOf(data)
    69  	}
    70  
    71  	state := &state{
    72  		helper: t.helper,
    73  		prep:   p,
    74  		tmpl:   tmpl,
    75  		wr:     wr,
    76  		vars:   []variable{{"$", value}},
    77  	}
    78  
    79  	return tmpl.executeWithState(state, value)
    80  
    81  }
    82  
    83  // Prepare returns a template ready for execution.
    84  func (t *Template) Prepare() (*Template, error) {
    85  	return t, nil
    86  }
    87  
    88  func (t *Template) executeWithState(state *state, value reflect.Value) (err error) {
    89  	defer errRecover(&err)
    90  	if t.Tree == nil || t.Root == nil {
    91  		state.errorf("%q is an incomplete or empty template", t.Name())
    92  	}
    93  	state.walk(value, t.Root)
    94  	return
    95  }
    96  
    97  // Below are modified structs etc. The changes are marked with "Added for Hugo."
    98  
    99  // state represents the state of an execution. It's not part of the
   100  // template so that multiple executions of the same template
   101  // can execute in parallel.
   102  type state struct {
   103  	tmpl   *Template
   104  	prep   Preparer   // Added for Hugo.
   105  	helper ExecHelper // Added for Hugo.
   106  	wr     io.Writer
   107  	node   parse.Node // current node, for errors
   108  	vars   []variable // push-down stack of variable values.
   109  	depth  int        // the height of the stack of executing templates.
   110  }
   111  
   112  func (s *state) evalFunction(dot reflect.Value, node *parse.IdentifierNode, cmd parse.Node, args []parse.Node, final reflect.Value) reflect.Value {
   113  	s.at(node)
   114  	name := node.Ident
   115  
   116  	var function reflect.Value
   117  	var ok bool
   118  	if s.helper != nil {
   119  		// Added for Hugo.
   120  		function, ok = s.helper.GetFunc(s.prep, name)
   121  	}
   122  
   123  	if !ok {
   124  		function, ok = findFunction(name, s.tmpl)
   125  	}
   126  
   127  	if !ok {
   128  		s.errorf("%q is not a defined function", name)
   129  	}
   130  	return s.evalCall(dot, function, cmd, name, args, final)
   131  }
   132  
   133  // evalField evaluates an expression like (.Field) or (.Field arg1 arg2).
   134  // The 'final' argument represents the return value from the preceding
   135  // value of the pipeline, if any.
   136  func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, args []parse.Node, final, receiver reflect.Value) reflect.Value {
   137  	if !receiver.IsValid() {
   138  		if s.tmpl.option.missingKey == mapError { // Treat invalid value as missing map key.
   139  			s.errorf("nil data; no entry for key %q", fieldName)
   140  		}
   141  		return zero
   142  	}
   143  	typ := receiver.Type()
   144  	receiver, isNil := indirect(receiver)
   145  	if receiver.Kind() == reflect.Interface && isNil {
   146  		// Calling a method on a nil interface can't work. The
   147  		// MethodByName method call below would panic.
   148  		s.errorf("nil pointer evaluating %s.%s", typ, fieldName)
   149  		return zero
   150  	}
   151  
   152  	// Unless it's an interface, need to get to a value of type *T to guarantee
   153  	// we see all methods of T and *T.
   154  	ptr := receiver
   155  	if ptr.Kind() != reflect.Interface && ptr.Kind() != reflect.Ptr && ptr.CanAddr() {
   156  		ptr = ptr.Addr()
   157  	}
   158  	// Added for Hugo.
   159  	var first reflect.Value
   160  	var method reflect.Value
   161  	if s.helper != nil {
   162  		method, first = s.helper.GetMethod(s.prep, ptr, fieldName)
   163  	} else {
   164  		method = ptr.MethodByName(fieldName)
   165  	}
   166  
   167  	if method.IsValid() {
   168  		if first != zero {
   169  			return s.evalCall(dot, method, node, fieldName, args, final, first)
   170  		}
   171  
   172  		return s.evalCall(dot, method, node, fieldName, args, final)
   173  	}
   174  
   175  	hasArgs := len(args) > 1 || final != missingVal
   176  	// It's not a method; must be a field of a struct or an element of a map.
   177  	switch receiver.Kind() {
   178  	case reflect.Struct:
   179  		tField, ok := receiver.Type().FieldByName(fieldName)
   180  		if ok {
   181  			field := receiver.FieldByIndex(tField.Index)
   182  			if tField.PkgPath != "" { // field is unexported
   183  				s.errorf("%s is an unexported field of struct type %s", fieldName, typ)
   184  			}
   185  			// If it's a function, we must call it.
   186  			if hasArgs {
   187  				s.errorf("%s has arguments but cannot be invoked as function", fieldName)
   188  			}
   189  			return field
   190  		}
   191  	case reflect.Map:
   192  		// If it's a map, attempt to use the field name as a key.
   193  		nameVal := reflect.ValueOf(fieldName)
   194  		if nameVal.Type().AssignableTo(receiver.Type().Key()) {
   195  			if hasArgs {
   196  				s.errorf("%s is not a method but has arguments", fieldName)
   197  			}
   198  			var result reflect.Value
   199  			if s.helper != nil {
   200  				// Added for Hugo.
   201  				result, _ = s.helper.GetMapValue(s.prep, receiver, nameVal)
   202  			} else {
   203  				result = receiver.MapIndex(nameVal)
   204  			}
   205  			if !result.IsValid() {
   206  				switch s.tmpl.option.missingKey {
   207  				case mapInvalid:
   208  					// Just use the invalid value.
   209  				case mapZeroValue:
   210  					result = reflect.Zero(receiver.Type().Elem())
   211  				case mapError:
   212  					s.errorf("map has no entry for key %q", fieldName)
   213  				}
   214  			}
   215  			return result
   216  		}
   217  	case reflect.Ptr:
   218  		etyp := receiver.Type().Elem()
   219  		if etyp.Kind() == reflect.Struct {
   220  			if _, ok := etyp.FieldByName(fieldName); !ok {
   221  				// If there's no such field, say "can't evaluate"
   222  				// instead of "nil pointer evaluating".
   223  				break
   224  			}
   225  		}
   226  		if isNil {
   227  			s.errorf("nil pointer evaluating %s.%s", typ, fieldName)
   228  		}
   229  	}
   230  	s.errorf("can't evaluate field %s in type %s", fieldName, typ)
   231  	panic("not reached")
   232  }
   233  
   234  // evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so
   235  // it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0]
   236  // as the function itself.
   237  func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, args []parse.Node, final reflect.Value, first ...reflect.Value) reflect.Value {
   238  	if args != nil {
   239  		args = args[1:] // Zeroth arg is function name/node; not passed to function.
   240  	}
   241  	typ := fun.Type()
   242  	numFirst := len(first)
   243  	numIn := len(args) + numFirst // // Added for Hugo
   244  	if final != missingVal {
   245  		numIn++
   246  	}
   247  	numFixed := len(args) + len(first)
   248  	if typ.IsVariadic() {
   249  		numFixed = typ.NumIn() - 1 // last arg is the variadic one.
   250  		if numIn < numFixed {
   251  			s.errorf("wrong number of args for %s: want at least %d got %d", name, typ.NumIn()-1, len(args))
   252  		}
   253  	} else if numIn != typ.NumIn() {
   254  		s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), numIn)
   255  	}
   256  	if !goodFunc(typ) {
   257  		// TODO: This could still be a confusing error; maybe goodFunc should provide info.
   258  		s.errorf("can't call method/function %q with %d results", name, typ.NumOut())
   259  	}
   260  	// Build the arg list.
   261  	argv := make([]reflect.Value, numIn)
   262  	// Args must be evaluated. Fixed args first.
   263  	i := len(first)
   264  	for ; i < numFixed && i < len(args)+numFirst; i++ {
   265  		argv[i] = s.evalArg(dot, typ.In(i), args[i-numFirst])
   266  	}
   267  	// Now the ... args.
   268  	if typ.IsVariadic() {
   269  		argType := typ.In(typ.NumIn() - 1).Elem() // Argument is a slice.
   270  		for ; i < len(args)+numFirst; i++ {
   271  			argv[i] = s.evalArg(dot, argType, args[i-numFirst])
   272  		}
   273  
   274  	}
   275  	// Add final value if necessary.
   276  	if final != missingVal {
   277  		t := typ.In(typ.NumIn() - 1)
   278  		if typ.IsVariadic() {
   279  			if numIn-1 < numFixed {
   280  				// The added final argument corresponds to a fixed parameter of the function.
   281  				// Validate against the type of the actual parameter.
   282  				t = typ.In(numIn - 1)
   283  			} else {
   284  				// The added final argument corresponds to the variadic part.
   285  				// Validate against the type of the elements of the variadic slice.
   286  				t = t.Elem()
   287  			}
   288  		}
   289  		argv[i] = s.validateType(final, t)
   290  	}
   291  
   292  	// Added for Hugo
   293  	for i := 0; i < len(first); i++ {
   294  		argv[i] = s.validateType(first[i], typ.In(i))
   295  	}
   296  
   297  	v, err := safeCall(fun, argv)
   298  	// If we have an error that is not nil, stop execution and return that
   299  	// error to the caller.
   300  	if err != nil {
   301  		s.at(node)
   302  		s.errorf("error calling %s: %v", name, err)
   303  	}
   304  	if v.Type() == reflectValueType {
   305  		v = v.Interface().(reflect.Value)
   306  	}
   307  	return v
   308  }
   309  
   310  func isTrue(val reflect.Value) (truth, ok bool) {
   311  	return hreflect.IsTruthfulValue(val), true
   312  }