github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/tpl/internal/go_templates/htmltemplate/content.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package template
     6  
     7  import (
     8  	"fmt"
     9  	htmltemplate "html/template"
    10  	"reflect"
    11  )
    12  
    13  type contentType uint8
    14  
    15  const (
    16  	contentTypePlain contentType = iota
    17  	contentTypeCSS
    18  	contentTypeHTML
    19  	contentTypeHTMLAttr
    20  	contentTypeJS
    21  	contentTypeJSStr
    22  	contentTypeURL
    23  	contentTypeSrcset
    24  	// contentTypeUnsafe is used in attr.go for values that affect how
    25  	// embedded content and network messages are formed, vetted,
    26  	// or interpreted; or which credentials network messages carry.
    27  	contentTypeUnsafe
    28  )
    29  
    30  // indirect returns the value, after dereferencing as many times
    31  // as necessary to reach the base type (or nil).
    32  func indirect(a interface{}) interface{} {
    33  	if a == nil {
    34  		return nil
    35  	}
    36  	if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
    37  		// Avoid creating a reflect.Value if it's not a pointer.
    38  		return a
    39  	}
    40  	v := reflect.ValueOf(a)
    41  	for v.Kind() == reflect.Ptr && !v.IsNil() {
    42  		v = v.Elem()
    43  	}
    44  	return v.Interface()
    45  }
    46  
    47  var (
    48  	errorType       = reflect.TypeOf((*error)(nil)).Elem()
    49  	fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
    50  )
    51  
    52  // indirectToStringerOrError returns the value, after dereferencing as many times
    53  // as necessary to reach the base type (or nil) or an implementation of fmt.Stringer
    54  // or error,
    55  func indirectToStringerOrError(a interface{}) interface{} {
    56  	if a == nil {
    57  		return nil
    58  	}
    59  	v := reflect.ValueOf(a)
    60  	for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() {
    61  		v = v.Elem()
    62  	}
    63  	return v.Interface()
    64  }
    65  
    66  // stringify converts its arguments to a string and the type of the content.
    67  // All pointers are dereferenced, as in the text/template package.
    68  func stringify(args ...interface{}) (string, contentType) {
    69  	if len(args) == 1 {
    70  		switch s := indirect(args[0]).(type) {
    71  		case string:
    72  			return s, contentTypePlain
    73  		case htmltemplate.CSS:
    74  			return string(s), contentTypeCSS
    75  		case htmltemplate.HTML:
    76  			return string(s), contentTypeHTML
    77  		case htmltemplate.HTMLAttr:
    78  			return string(s), contentTypeHTMLAttr
    79  		case htmltemplate.JS:
    80  			return string(s), contentTypeJS
    81  		case htmltemplate.JSStr:
    82  			return string(s), contentTypeJSStr
    83  		case htmltemplate.URL:
    84  			return string(s), contentTypeURL
    85  		case htmltemplate.Srcset:
    86  			return string(s), contentTypeSrcset
    87  		}
    88  	}
    89  	i := 0
    90  	for _, arg := range args {
    91  		// We skip untyped nil arguments for backward compatibility.
    92  		// Without this they would be output as <nil>, escaped.
    93  		// See issue 25875.
    94  		if arg == nil {
    95  			continue
    96  		}
    97  
    98  		args[i] = indirectToStringerOrError(arg)
    99  		i++
   100  	}
   101  	return fmt.Sprint(args[:i]...), contentTypePlain
   102  }