package template
import (
)
type FuncMap map[string]any
func () FuncMap {
return FuncMap{
"and": and,
"call": call,
"html": HTMLEscaper,
"index": index,
"slice": slice,
"js": JSEscaper,
"len": length,
"not": not,
"or": or,
"print": fmt.Sprint,
"printf": fmt.Sprintf,
"println": fmt.Sprintln,
"urlquery": URLQueryEscaper,
"eq": eq,
"ge": ge,
"gt": gt,
"le": le,
"lt": lt,
"ne": ne,
}
}
var builtinFuncsOnce struct {
sync.Once
v map[string]reflect.Value
}
func () map[string]reflect.Value {
builtinFuncsOnce.Do(func() {
builtinFuncsOnce.v = createValueFuncs(builtins())
})
return builtinFuncsOnce.v
}
func ( FuncMap) map[string]reflect.Value {
:= make(map[string]reflect.Value)
addValueFuncs(, )
return
}
func ( map[string]reflect.Value, FuncMap) {
for , := range {
if !goodName() {
panic(fmt.Errorf("function name %q is not a valid identifier", ))
}
:= reflect.ValueOf()
if .Kind() != reflect.Func {
panic("value for " + + " not a function")
}
if !goodFunc(.Type()) {
panic(fmt.Errorf("can't install method/function %q with %d results", , .Type().NumOut()))
}
[] =
}
}
func (, FuncMap) {
for , := range {
[] =
}
}
func ( reflect.Type) bool {
switch {
case .NumOut() == 1:
return true
case .NumOut() == 2 && .Out(1) == errorType:
return true
}
return false
}
func ( string) bool {
if == "" {
return false
}
for , := range {
switch {
case == '_':
case == 0 && !unicode.IsLetter():
return false
case !unicode.IsLetter() && !unicode.IsDigit():
return false
}
}
return true
}
func ( string, *Template) ( reflect.Value, , bool) {
if != nil && .common != nil {
.muFuncs.RLock()
defer .muFuncs.RUnlock()
if := .execFuncs[]; .IsValid() {
return , false, true
}
}
if := builtinFuncs()[]; .IsValid() {
return , true, true
}
return reflect.Value{}, false, false
}
func ( reflect.Value, reflect.Type) (reflect.Value, error) {
if !.IsValid() {
if !canBeNil() {
return reflect.Value{}, fmt.Errorf("value is nil; should be of type %s", )
}
= reflect.Zero()
}
if .Type().AssignableTo() {
return , nil
}
if intLike(.Kind()) && intLike(.Kind()) && .Type().ConvertibleTo() {
= .Convert()
return , nil
}
return reflect.Value{}, fmt.Errorf("value has type %s; should be %s", .Type(), )
}
func ( reflect.Kind) bool {
switch {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return true
}
return false
}
func ( reflect.Value, int) (int, error) {
var int64
switch .Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
= .Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
= int64(.Uint())
case reflect.Invalid:
return 0, fmt.Errorf("cannot index slice/array with nil")
default:
return 0, fmt.Errorf("cannot index slice/array with type %s", .Type())
}
if < 0 || int() < 0 || int() > {
return 0, fmt.Errorf("index out of range: %d", )
}
return int(), nil
}
func ( reflect.Value, ...reflect.Value) (reflect.Value, error) {
= indirectInterface()
if !.IsValid() {
return reflect.Value{}, fmt.Errorf("index of untyped nil")
}
for , := range {
= indirectInterface()
var bool
if , = indirect(); {
return reflect.Value{}, fmt.Errorf("index of nil pointer")
}
switch .Kind() {
case reflect.Array, reflect.Slice, reflect.String:
, := indexArg(, .Len())
if != nil {
return reflect.Value{},
}
= .Index()
case reflect.Map:
, := prepareArg(, .Type().Key())
if != nil {
return reflect.Value{},
}
if := .MapIndex(); .IsValid() {
=
} else {
= reflect.Zero(.Type().Elem())
}
case reflect.Invalid:
panic("unreachable")
default:
return reflect.Value{}, fmt.Errorf("can't index item of type %s", .Type())
}
}
return , nil
}
func ( reflect.Value, ...reflect.Value) (reflect.Value, error) {
= indirectInterface()
if !.IsValid() {
return reflect.Value{}, fmt.Errorf("slice of untyped nil")
}
if len() > 3 {
return reflect.Value{}, fmt.Errorf("too many slice indexes: %d", len())
}
var int
switch .Kind() {
case reflect.String:
if len() == 3 {
return reflect.Value{}, fmt.Errorf("cannot 3-index slice a string")
}
= .Len()
case reflect.Array, reflect.Slice:
= .Cap()
default:
return reflect.Value{}, fmt.Errorf("can't slice item of type %s", .Type())
}
:= [3]int{0, .Len()}
for , := range {
, := indexArg(, )
if != nil {
return reflect.Value{},
}
[] =
}
if [0] > [1] {
return reflect.Value{}, fmt.Errorf("invalid slice index: %d > %d", [0], [1])
}
if len() < 3 {
return .Slice([0], [1]), nil
}
if [1] > [2] {
return reflect.Value{}, fmt.Errorf("invalid slice index: %d > %d", [1], [2])
}
return .Slice3([0], [1], [2]), nil
}
func ( reflect.Value) (int, error) {
, := indirect()
if {
return 0, fmt.Errorf("len of nil pointer")
}
switch .Kind() {
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
return .Len(), nil
}
return 0, fmt.Errorf("len of type %s", .Type())
}
func ( reflect.Value, ...reflect.Value) (reflect.Value, error) {
= indirectInterface()
if !.IsValid() {
return reflect.Value{}, fmt.Errorf("call of nil")
}
:= .Type()
if .Kind() != reflect.Func {
return reflect.Value{}, fmt.Errorf("non-function of type %s", )
}
if !goodFunc() {
return reflect.Value{}, fmt.Errorf("function called with %d args; should be 1 or 2", .NumOut())
}
:= .NumIn()
var reflect.Type
if .IsVariadic() {
if len() < -1 {
return reflect.Value{}, fmt.Errorf("wrong number of args: got %d want at least %d", len(), -1)
}
= .In( - 1).Elem()
} else {
if len() != {
return reflect.Value{}, fmt.Errorf("wrong number of args: got %d want %d", len(), )
}
}
:= make([]reflect.Value, len())
for , := range {
= indirectInterface()
:=
if !.IsVariadic() || < -1 {
= .In()
}
var error
if [], = prepareArg(, ); != nil {
return reflect.Value{}, fmt.Errorf("arg %d: %w", , )
}
}
return safeCall(, )
}
func ( reflect.Value, []reflect.Value) ( reflect.Value, error) {
defer func() {
if := recover(); != nil {
if , := .(error); {
=
} else {
= fmt.Errorf("%v", )
}
}
}()
:= .Call()
if len() == 2 && ![1].IsNil() {
return [0], [1].Interface().(error)
}
return [0], nil
}
func ( reflect.Value) bool {
, := isTrue(indirectInterface())
return
}
func ( reflect.Value, ...reflect.Value) reflect.Value {
panic("unreachable")
}
func ( reflect.Value, ...reflect.Value) reflect.Value {
panic("unreachable")
}
func ( reflect.Value) bool {
return !truth()
}
var (
errBadComparisonType = errors.New("invalid type for comparison")
errBadComparison = errors.New("incompatible types for comparison")
errNoComparison = errors.New("missing argument for comparison")
)
type kind int
const (
invalidKind kind = iota
boolKind
complexKind
intKind
floatKind
stringKind
uintKind
)
func ( reflect.Value) (kind, error) {
switch .Kind() {
case reflect.Bool:
return boolKind, nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return intKind, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return uintKind, nil
case reflect.Float32, reflect.Float64:
return floatKind, nil
case reflect.Complex64, reflect.Complex128:
return complexKind, nil
case reflect.String:
return stringKind, nil
}
return invalidKind, errBadComparisonType
}
func ( reflect.Value, ...reflect.Value) (bool, error) {
= indirectInterface()
if != zero {
if := .Type(); !.Comparable() {
return false, fmt.Errorf("uncomparable type %s: %v", , )
}
}
if len() == 0 {
return false, errNoComparison
}
, := basicKind()
for , := range {
= indirectInterface()
, := basicKind()
:= false
if != {
switch {
case == intKind && == uintKind:
= .Int() >= 0 && uint64(.Int()) == .Uint()
case == uintKind && == intKind:
= .Int() >= 0 && .Uint() == uint64(.Int())
default:
if != zero && != zero {
return false, errBadComparison
}
}
} else {
switch {
case boolKind:
= .Bool() == .Bool()
case complexKind:
= .Complex() == .Complex()
case floatKind:
= .Float() == .Float()
case intKind:
= .Int() == .Int()
case stringKind:
= .String() == .String()
case uintKind:
= .Uint() == .Uint()
default:
if == zero || == zero {
= ==
} else {
if := .Type(); !.Comparable() {
return false, fmt.Errorf("uncomparable type %s: %v", , )
}
= .Interface() == .Interface()
}
}
}
if {
return true, nil
}
}
return false, nil
}
func (, reflect.Value) (bool, error) {
, := eq(, )
return !,
}
func (, reflect.Value) (bool, error) {
= indirectInterface()
, := basicKind()
if != nil {
return false,
}
= indirectInterface()
, := basicKind()
if != nil {
return false,
}
:= false
if != {
switch {
case == intKind && == uintKind:
= .Int() < 0 || uint64(.Int()) < .Uint()
case == uintKind && == intKind:
= .Int() >= 0 && .Uint() < uint64(.Int())
default:
return false, errBadComparison
}
} else {
switch {
case boolKind, complexKind:
return false, errBadComparisonType
case floatKind:
= .Float() < .Float()
case intKind:
= .Int() < .Int()
case stringKind:
= .String() < .String()
case uintKind:
= .Uint() < .Uint()
default:
panic("invalid kind")
}
}
return , nil
}
func (, reflect.Value) (bool, error) {
, := lt(, )
if || != nil {
return ,
}
return eq(, )
}
func (, reflect.Value) (bool, error) {
, := le(, )
if != nil {
return false,
}
return !, nil
}
func (, reflect.Value) (bool, error) {
, := lt(, )
if != nil {
return false,
}
return !, nil
}
var (
htmlQuot = []byte(""")
htmlApos = []byte("'")
htmlAmp = []byte("&")
htmlLt = []byte("<")
htmlGt = []byte(">")
htmlNull = []byte("\uFFFD")
)
func ( io.Writer, []byte) {
:= 0
for , := range {
var []byte
switch {
case '\000':
= htmlNull
case '"':
= htmlQuot
case '\'':
= htmlApos
case '&':
= htmlAmp
case '<':
= htmlLt
case '>':
= htmlGt
default:
continue
}
.Write([:])
.Write()
= + 1
}
.Write([:])
}
func ( string) string {
if !strings.ContainsAny(, "'\"&<>\000") {
return
}
var bytes.Buffer
HTMLEscape(&, []byte())
return .String()
}
func ( ...any) string {
return HTMLEscapeString(evalArgs())
}
var (
jsLowUni = []byte(`\u00`)
hex = []byte("0123456789ABCDEF")
jsBackslash = []byte(`\\`)
jsApos = []byte(`\'`)
jsQuot = []byte(`\"`)
jsLt = []byte(`\u003C`)
jsGt = []byte(`\u003E`)
jsAmp = []byte(`\u0026`)
jsEq = []byte(`\u003D`)
)
func ( io.Writer, []byte) {
:= 0
for := 0; < len(); ++ {
:= []
if !jsIsSpecial(rune()) {
continue
}
.Write([:])
if < utf8.RuneSelf {
switch {
case '\\':
.Write(jsBackslash)
case '\'':
.Write(jsApos)
case '"':
.Write(jsQuot)
case '<':
.Write(jsLt)
case '>':
.Write(jsGt)
case '&':
.Write(jsAmp)
case '=':
.Write(jsEq)
default:
.Write(jsLowUni)
, := >>4, &0x0f
.Write(hex[ : +1])
.Write(hex[ : +1])
}
} else {
, := utf8.DecodeRune([:])
if unicode.IsPrint() {
.Write([ : +])
} else {
fmt.Fprintf(, "\\u%04X", )
}
+= - 1
}
= + 1
}
.Write([:])
}
func ( string) string {
if strings.IndexFunc(, jsIsSpecial) < 0 {
return
}
var bytes.Buffer
JSEscape(&, []byte())
return .String()
}
func ( rune) bool {
switch {
case '\\', '\'', '"', '<', '>', '&', '=':
return true
}
return < ' ' || utf8.RuneSelf <=
}
func ( ...any) string {
return JSEscapeString(evalArgs())
}
func ( ...any) string {
return url.QueryEscape(evalArgs())
}
func ( []any) string {
:= false
var string
if len() == 1 {
, = [0].(string)
}
if ! {
for , := range {
, := printableValue(reflect.ValueOf())
if {
[] =
}
}
= fmt.Sprint(...)
}
return
}