ref: be2097e1ad789eca5d893805a059d94defbe5c48
parent: 42dcaabf4f81ccd8aa831049440bb0c5d85707d6
author: bep <[email protected]>
date: Sun Apr 5 17:03:16 EDT 2015
tpl: split template.go The template funcs get their own file. This prevents having to scroll miles to get to the template infrastructure.
--- a/tpl/template.go
+++ b/tpl/template.go
@@ -15,30 +15,22 @@
import (
"bytes"
- "errors"
- "fmt"
"github.com/eknkc/amber"
- "github.com/spf13/cast"
bp "github.com/spf13/hugo/bufferpool"
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugofs"
jww "github.com/spf13/jwalterweatherman"
"github.com/yosssi/ace"
- "html"
"html/template"
"io"
"io/ioutil"
"os"
"path/filepath"
- "reflect"
- "sort"
- "strconv"
"strings"
)
var localTemplates *template.Template
var tmpl Template
-var funcMap template.FuncMap
type Template interface {
ExecuteTemplate(wr io.Writer, name string, data interface{}) error
@@ -93,1123 +85,6 @@
return templates
}
-func Eq(x, y interface{}) bool {
- normalize := func(v interface{}) interface{} {
- vv := reflect.ValueOf(v)
- switch vv.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return vv.Int()
- case reflect.Float32, reflect.Float64:
- return vv.Float()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- return vv.Uint()
- default:
- return v
- }
- }
- x = normalize(x)
- y = normalize(y)
- return reflect.DeepEqual(x, y)
-}
-
-func Ne(x, y interface{}) bool {
- return !Eq(x, y)
-}
-
-func Ge(a, b interface{}) bool {
- left, right := compareGetFloat(a, b)
- return left >= right
-}
-
-func Gt(a, b interface{}) bool {
- left, right := compareGetFloat(a, b)
- return left > right
-}
-
-func Le(a, b interface{}) bool {
- left, right := compareGetFloat(a, b)
- return left <= right
-}
-
-func Lt(a, b interface{}) bool {
- left, right := compareGetFloat(a, b)
- return left < right
-}
-
-func compareGetFloat(a interface{}, b interface{}) (float64, float64) {
- var left, right float64
- var leftStr, rightStr *string
- var err error
- av := reflect.ValueOf(a)
-
- switch av.Kind() {
- case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
- left = float64(av.Len())
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- left = float64(av.Int())
- case reflect.Float32, reflect.Float64:
- left = av.Float()
- case reflect.String:
- left, err = strconv.ParseFloat(av.String(), 64)
- if err != nil {
- str := av.String()
- leftStr = &str
- }
- }
-
- bv := reflect.ValueOf(b)
-
- switch bv.Kind() {
- case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
- right = float64(bv.Len())
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- right = float64(bv.Int())
- case reflect.Float32, reflect.Float64:
- right = bv.Float()
- case reflect.String:
- right, err = strconv.ParseFloat(bv.String(), 64)
- if err != nil {
- str := bv.String()
- rightStr = &str
- }
-
- }
-
- switch {
- case leftStr == nil || rightStr == nil:
- case *leftStr < *rightStr:
- return 0, 1
- case *leftStr > *rightStr:
- return 1, 0
- default:
- return 0, 0
- }
-
- return left, right
-}
-
-// Slicing in Slicestr is done by specifying a half-open range with
-// two indices, start and end. 1 and 4 creates a slice including elements 1 through 3.
-// The end index can be omitted, it defaults to the string's length.
-func Slicestr(a interface{}, startEnd ...int) (string, error) {
- aStr, err := cast.ToStringE(a)
- if err != nil {
- return "", err
- }
-
- if len(startEnd) > 2 {
- return "", errors.New("too many arguments")
- }
-
- if len(startEnd) == 2 {
- return aStr[startEnd[0]:startEnd[1]], nil
- } else if len(startEnd) == 1 {
- return aStr[startEnd[0]:], nil
- } else {
- return aStr[:], nil
- }
-
-}
-
-// Substr extracts parts of a string, beginning at the character at the specified
-// position, and returns the specified number of characters.
-//
-// It normally takes two parameters: start and length.
-// It can also take one parameter: start, i.e. length is omitted, in which case
-// the substring starting from start until the end of the string will be returned.
-//
-// To extract characters from the end of the string, use a negative start number.
-//
-// In addition, borrowing from the extended behavior described at http://php.net/substr,
-// if length is given and is negative, then that many characters will be omitted from
-// the end of string.
-func Substr(a interface{}, nums ...int) (string, error) {
- aStr, err := cast.ToStringE(a)
- if err != nil {
- return "", err
- }
-
- var start, length int
- switch len(nums) {
- case 1:
- start = nums[0]
- length = len(aStr)
- case 2:
- start = nums[0]
- length = nums[1]
- default:
- return "", errors.New("too many arguments")
- }
-
- if start < -len(aStr) {
- start = 0
- }
- if start > len(aStr) {
- return "", errors.New(fmt.Sprintf("start position out of bounds for %d-byte string", len(aStr)))
- }
-
- var s, e int
- if start >= 0 && length >= 0 {
- s = start
- e = start + length
- } else if start < 0 && length >= 0 {
- s = len(aStr) + start - length + 1
- e = len(aStr) + start + 1
- } else if start >= 0 && length < 0 {
- s = start
- e = len(aStr) + length
- } else {
- s = len(aStr) + start
- e = len(aStr) + length
- }
-
- if s > e {
- return "", errors.New(fmt.Sprintf("calculated start position greater than end position: %d > %d", s, e))
- }
- if e > len(aStr) {
- e = len(aStr)
- }
-
- return aStr[s:e], nil
-
-}
-
-func Split(a interface{}, delimiter string) ([]string, error) {
- aStr, err := cast.ToStringE(a)
- if err != nil {
- return []string{}, err
- }
- return strings.Split(aStr, delimiter), nil
-}
-
-func Intersect(l1, l2 interface{}) (interface{}, error) {
- if l1 == nil || l2 == nil {
- return make([]interface{}, 0), nil
- }
-
- l1v := reflect.ValueOf(l1)
- l2v := reflect.ValueOf(l2)
-
- switch l1v.Kind() {
- case reflect.Array, reflect.Slice:
- switch l2v.Kind() {
- case reflect.Array, reflect.Slice:
- r := reflect.MakeSlice(l1v.Type(), 0, 0)
- for i := 0; i < l1v.Len(); i++ {
- l1vv := l1v.Index(i)
- for j := 0; j < l2v.Len(); j++ {
- l2vv := l2v.Index(j)
- switch l1vv.Kind() {
- case reflect.String:
- if l1vv.Type() == l2vv.Type() && l1vv.String() == l2vv.String() && !In(r, l2vv) {
- r = reflect.Append(r, l2vv)
- }
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- switch l2vv.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- if l1vv.Int() == l2vv.Int() && !In(r, l2vv) {
- r = reflect.Append(r, l2vv)
- }
- }
- case reflect.Float32, reflect.Float64:
- switch l2vv.Kind() {
- case reflect.Float32, reflect.Float64:
- if l1vv.Float() == l2vv.Float() && !In(r, l2vv) {
- r = reflect.Append(r, l2vv)
- }
- }
- }
- }
- }
- return r.Interface(), nil
- default:
- return nil, errors.New("can't iterate over " + reflect.ValueOf(l2).Type().String())
- }
- default:
- return nil, errors.New("can't iterate over " + reflect.ValueOf(l1).Type().String())
- }
-}
-
-func In(l interface{}, v interface{}) bool {
- lv := reflect.ValueOf(l)
- vv := reflect.ValueOf(v)
-
- switch lv.Kind() {
- case reflect.Array, reflect.Slice:
- for i := 0; i < lv.Len(); i++ {
- lvv := lv.Index(i)
- switch lvv.Kind() {
- case reflect.String:
- if vv.Type() == lvv.Type() && vv.String() == lvv.String() {
- return true
- }
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- switch vv.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- if vv.Int() == lvv.Int() {
- return true
- }
- }
- case reflect.Float32, reflect.Float64:
- switch vv.Kind() {
- case reflect.Float32, reflect.Float64:
- if vv.Float() == lvv.Float() {
- return true
- }
- }
- }
- }
- case reflect.String:
- if vv.Type() == lv.Type() && strings.Contains(lv.String(), vv.String()) {
- return true
- }
- }
- return false
-}
-
-// indirect is taken from 'text/template/exec.go'
-func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
- for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() {
- if v.IsNil() {
- return v, true
- }
- if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
- break
- }
- }
- return v, false
-}
-
-// First is exposed to templates, to iterate over the first N items in a
-// rangeable list.
-func First(limit interface{}, seq interface{}) (interface{}, error) {
-
- if limit == nil || seq == nil {
- return nil, errors.New("both limit and seq must be provided")
- }
-
- limitv, err := cast.ToIntE(limit)
-
- if err != nil {
- return nil, err
- }
-
- if limitv < 1 {
- return nil, errors.New("can't return negative/empty count of items from sequence")
- }
-
- seqv := reflect.ValueOf(seq)
- seqv, isNil := indirect(seqv)
- if isNil {
- return nil, errors.New("can't iterate over a nil value")
- }
-
- switch seqv.Kind() {
- case reflect.Array, reflect.Slice, reflect.String:
- // okay
- default:
- return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
- }
- if limitv > seqv.Len() {
- limitv = seqv.Len()
- }
- return seqv.Slice(0, limitv).Interface(), nil
-}
-
-var (
- zero reflect.Value
- errorType = reflect.TypeOf((*error)(nil)).Elem()
-)
-
-func evaluateSubElem(obj reflect.Value, elemName string) (reflect.Value, error) {
- if !obj.IsValid() {
- return zero, errors.New("can't evaluate an invalid value")
- }
- typ := obj.Type()
- obj, isNil := indirect(obj)
-
- // first, check whether obj has a method. In this case, obj is
- // an interface, a struct or its pointer. If obj is a struct,
- // to check all T and *T method, use obj pointer type Value
- objPtr := obj
- if objPtr.Kind() != reflect.Interface && objPtr.CanAddr() {
- objPtr = objPtr.Addr()
- }
- mt, ok := objPtr.Type().MethodByName(elemName)
- if ok {
- if mt.PkgPath != "" {
- return zero, fmt.Errorf("%s is an unexported method of type %s", elemName, typ)
- }
- // struct pointer has one receiver argument and interface doesn't have an argument
- if mt.Type.NumIn() > 1 || mt.Type.NumOut() == 0 || mt.Type.NumOut() > 2 {
- return zero, fmt.Errorf("%s is a method of type %s but doesn't satisfy requirements", elemName, typ)
- }
- if mt.Type.NumOut() == 1 && mt.Type.Out(0).Implements(errorType) {
- return zero, fmt.Errorf("%s is a method of type %s but doesn't satisfy requirements", elemName, typ)
- }
- if mt.Type.NumOut() == 2 && !mt.Type.Out(1).Implements(errorType) {
- return zero, fmt.Errorf("%s is a method of type %s but doesn't satisfy requirements", elemName, typ)
- }
- res := objPtr.Method(mt.Index).Call([]reflect.Value{})
- if len(res) == 2 && !res[1].IsNil() {
- return zero, fmt.Errorf("error at calling a method %s of type %s: %s", elemName, typ, res[1].Interface().(error))
- }
- return res[0], nil
- }
-
- // elemName isn't a method so next start to check whether it is
- // a struct field or a map value. In both cases, it mustn't be
- // a nil value
- if isNil {
- return zero, fmt.Errorf("can't evaluate a nil pointer of type %s by a struct field or map key name %s", typ, elemName)
- }
- switch obj.Kind() {
- case reflect.Struct:
- ft, ok := obj.Type().FieldByName(elemName)
- if ok {
- if ft.PkgPath != "" {
- return zero, fmt.Errorf("%s is an unexported field of struct type %s", elemName, typ)
- }
- return obj.FieldByIndex(ft.Index), nil
- }
- return zero, fmt.Errorf("%s isn't a field of struct type %s", elemName, typ)
- case reflect.Map:
- kv := reflect.ValueOf(elemName)
- if kv.Type().AssignableTo(obj.Type().Key()) {
- return obj.MapIndex(kv), nil
- }
- return zero, fmt.Errorf("%s isn't a key of map type %s", elemName, typ)
- }
- return zero, fmt.Errorf("%s is neither a struct field, a method nor a map element of type %s", elemName, typ)
-}
-
-func checkCondition(v, mv reflect.Value, op string) (bool, error) {
- if !v.IsValid() || !mv.IsValid() {
- return false, nil
- }
-
- var isNil bool
- v, isNil = indirect(v)
- if isNil {
- return false, nil
- }
- mv, isNil = indirect(mv)
- if isNil {
- return false, nil
- }
-
- var ivp, imvp *int64
- var svp, smvp *string
- var ima []int64
- var sma []string
- if mv.Type() == v.Type() {
- switch v.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- iv := v.Int()
- ivp = &iv
- imv := mv.Int()
- imvp = &imv
- case reflect.String:
- sv := v.String()
- svp = &sv
- smv := mv.String()
- smvp = &smv
- }
- } else {
- if mv.Kind() != reflect.Array && mv.Kind() != reflect.Slice {
- return false, nil
- }
- if mv.Type().Elem() != v.Type() {
- return false, nil
- }
- switch v.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- iv := v.Int()
- ivp = &iv
- for i := 0; i < mv.Len(); i++ {
- ima = append(ima, mv.Index(i).Int())
- }
- case reflect.String:
- sv := v.String()
- svp = &sv
- for i := 0; i < mv.Len(); i++ {
- sma = append(sma, mv.Index(i).String())
- }
- }
- }
-
- switch op {
- case "", "=", "==", "eq":
- if ivp != nil && imvp != nil {
- return *ivp == *imvp, nil
- } else if svp != nil && smvp != nil {
- return *svp == *smvp, nil
- }
- case "!=", "<>", "ne":
- if ivp != nil && imvp != nil {
- return *ivp != *imvp, nil
- } else if svp != nil && smvp != nil {
- return *svp != *smvp, nil
- }
- case ">=", "ge":
- if ivp != nil && imvp != nil {
- return *ivp >= *imvp, nil
- } else if svp != nil && smvp != nil {
- return *svp >= *smvp, nil
- }
- case ">", "gt":
- if ivp != nil && imvp != nil {
- return *ivp > *imvp, nil
- } else if svp != nil && smvp != nil {
- return *svp > *smvp, nil
- }
- case "<=", "le":
- if ivp != nil && imvp != nil {
- return *ivp <= *imvp, nil
- } else if svp != nil && smvp != nil {
- return *svp <= *smvp, nil
- }
- case "<", "lt":
- if ivp != nil && imvp != nil {
- return *ivp < *imvp, nil
- } else if svp != nil && smvp != nil {
- return *svp < *smvp, nil
- }
- case "in", "not in":
- var r bool
- if ivp != nil && len(ima) > 0 {
- r = In(ima, *ivp)
- } else if svp != nil {
- if len(sma) > 0 {
- r = In(sma, *svp)
- } else if smvp != nil {
- r = In(*smvp, *svp)
- }
- } else {
- return false, nil
- }
- if op == "not in" {
- return !r, nil
- } else {
- return r, nil
- }
- default:
- return false, errors.New("no such an operator")
- }
- return false, nil
-}
-
-func Where(seq, key interface{}, args ...interface{}) (r interface{}, err error) {
- seqv := reflect.ValueOf(seq)
- kv := reflect.ValueOf(key)
-
- var mv reflect.Value
- var op string
- switch len(args) {
- case 1:
- mv = reflect.ValueOf(args[0])
- case 2:
- var ok bool
- if op, ok = args[0].(string); !ok {
- return nil, errors.New("operator argument must be string type")
- }
- op = strings.TrimSpace(strings.ToLower(op))
- mv = reflect.ValueOf(args[1])
- default:
- return nil, errors.New("can't evaluate the array by no match argument or more than or equal to two arguments")
- }
-
- seqv, isNil := indirect(seqv)
- if isNil {
- return nil, errors.New("can't iterate over a nil value of type " + reflect.ValueOf(seq).Type().String())
- }
-
- var path []string
- if kv.Kind() == reflect.String {
- path = strings.Split(strings.Trim(kv.String(), "."), ".")
- }
-
- switch seqv.Kind() {
- case reflect.Array, reflect.Slice:
- rv := reflect.MakeSlice(seqv.Type(), 0, 0)
- for i := 0; i < seqv.Len(); i++ {
- var vvv reflect.Value
- rvv := seqv.Index(i)
- if kv.Kind() == reflect.String {
- vvv = rvv
- for _, elemName := range path {
- vvv, err = evaluateSubElem(vvv, elemName)
- if err != nil {
- return nil, err
- }
- }
- } else {
- vv, _ := indirect(rvv)
- if vv.Kind() == reflect.Map && kv.Type().AssignableTo(vv.Type().Key()) {
- vvv = vv.MapIndex(kv)
- }
- }
- if ok, err := checkCondition(vvv, mv, op); ok {
- rv = reflect.Append(rv, rvv)
- } else if err != nil {
- return nil, err
- }
- }
- return rv.Interface(), nil
- default:
- return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
- }
-}
-
-// Apply, given a map, array, or slice, returns a new slice with the function fname applied over it.
-func Apply(seq interface{}, fname string, args ...interface{}) (interface{}, error) {
- if seq == nil {
- return make([]interface{}, 0), nil
- }
-
- if fname == "apply" {
- return nil, errors.New("can't apply myself (no turtles allowed)")
- }
-
- seqv := reflect.ValueOf(seq)
- seqv, isNil := indirect(seqv)
- if isNil {
- return nil, errors.New("can't iterate over a nil value")
- }
-
- fn, found := funcMap[fname]
- if !found {
- return nil, errors.New("can't find function " + fname)
- }
-
- fnv := reflect.ValueOf(fn)
-
- switch seqv.Kind() {
- case reflect.Array, reflect.Slice:
- r := make([]interface{}, seqv.Len())
- for i := 0; i < seqv.Len(); i++ {
- vv := seqv.Index(i)
-
- vvv, err := applyFnToThis(fnv, vv, args...)
-
- if err != nil {
- return nil, err
- }
-
- r[i] = vvv.Interface()
- }
-
- return r, nil
- default:
- return nil, errors.New("can't apply over " + reflect.ValueOf(seq).Type().String())
- }
-}
-
-func applyFnToThis(fn, this reflect.Value, args ...interface{}) (reflect.Value, error) {
- n := make([]reflect.Value, len(args))
- for i, arg := range args {
- if arg == "." {
- n[i] = this
- } else {
- n[i] = reflect.ValueOf(arg)
- }
- }
-
- res := fn.Call(n)
-
- if len(res) == 1 || res[1].IsNil() {
- return res[0], nil
- } else {
- return reflect.ValueOf(nil), res[1].Interface().(error)
- }
-}
-
-func Delimit(seq, delimiter interface{}, last ...interface{}) (template.HTML, error) {
- d, err := cast.ToStringE(delimiter)
- if err != nil {
- return "", err
- }
-
- var dLast *string
- for _, l := range last {
- dStr, err := cast.ToStringE(l)
- if err != nil {
- dLast = nil
- }
- dLast = &dStr
- break
- }
-
- seqv := reflect.ValueOf(seq)
- seqv, isNil := indirect(seqv)
- if isNil {
- return "", errors.New("can't iterate over a nil value")
- }
-
- var str string
- switch seqv.Kind() {
- case reflect.Map:
- sortSeq, err := Sort(seq)
- if err != nil {
- return "", err
- }
- seqv = reflect.ValueOf(sortSeq)
- fallthrough
- case reflect.Array, reflect.Slice, reflect.String:
- for i := 0; i < seqv.Len(); i++ {
- val := seqv.Index(i).Interface()
- valStr, err := cast.ToStringE(val)
- if err != nil {
- continue
- }
- switch {
- case i == seqv.Len()-2 && dLast != nil:
- str += valStr + *dLast
- case i == seqv.Len()-1:
- str += valStr
- default:
- str += valStr + d
- }
- }
-
- default:
- return "", errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
- }
-
- return template.HTML(str), nil
-}
-
-func Sort(seq interface{}, args ...interface{}) ([]interface{}, error) {
- seqv := reflect.ValueOf(seq)
- seqv, isNil := indirect(seqv)
- if isNil {
- return nil, errors.New("can't iterate over a nil value")
- }
-
- // Create a list of pairs that will be used to do the sort
- p := pairList{SortAsc: true}
- p.Pairs = make([]pair, seqv.Len())
-
- for i, l := range args {
- dStr, err := cast.ToStringE(l)
- switch {
- case i == 0 && err != nil:
- p.SortByField = ""
- case i == 0 && err == nil:
- p.SortByField = dStr
- case i == 1 && err == nil && dStr == "desc":
- p.SortAsc = false
- case i == 1:
- p.SortAsc = true
- }
- }
-
- var sorted []interface{}
- switch seqv.Kind() {
- case reflect.Array, reflect.Slice:
- for i := 0; i < seqv.Len(); i++ {
- p.Pairs[i].Key = reflect.ValueOf(i)
- p.Pairs[i].Value = seqv.Index(i)
- }
- if p.SortByField == "" {
- p.SortByField = "value"
- }
-
- case reflect.Map:
- keys := seqv.MapKeys()
- for i := 0; i < seqv.Len(); i++ {
- p.Pairs[i].Key = keys[i]
- p.Pairs[i].Value = seqv.MapIndex(keys[i])
- }
-
- default:
- return nil, errors.New("can't sort " + reflect.ValueOf(seq).Type().String())
- }
- sorted = p.sort()
- return sorted, nil
-}
-
-// Credit for pair sorting method goes to Andrew Gerrand
-// https://groups.google.com/forum/#!topic/golang-nuts/FT7cjmcL7gw
-// A data structure to hold a key/value pair.
-type pair struct {
- Key reflect.Value
- Value reflect.Value
-}
-
-// A slice of pairs that implements sort.Interface to sort by Value.
-type pairList struct {
- Pairs []pair
- SortByField string
- SortAsc bool
-}
-
-func (p pairList) Swap(i, j int) { p.Pairs[i], p.Pairs[j] = p.Pairs[j], p.Pairs[i] }
-func (p pairList) Len() int { return len(p.Pairs) }
-func (p pairList) Less(i, j int) bool {
- var truth bool
- switch {
- case p.SortByField == "value":
- iVal := p.Pairs[i].Value
- jVal := p.Pairs[j].Value
- truth = Lt(iVal.Interface(), jVal.Interface())
-
- case p.SortByField != "":
- if p.Pairs[i].Value.FieldByName(p.SortByField).IsValid() {
- iVal := p.Pairs[i].Value.FieldByName(p.SortByField)
- jVal := p.Pairs[j].Value.FieldByName(p.SortByField)
- truth = Lt(iVal.Interface(), jVal.Interface())
- }
- default:
- iVal := p.Pairs[i].Key
- jVal := p.Pairs[j].Key
- truth = Lt(iVal.Interface(), jVal.Interface())
- }
- return truth
-}
-
-// sorts a pairList and returns a slice of sorted values
-func (p pairList) sort() []interface{} {
- if p.SortAsc {
- sort.Sort(p)
- } else {
- sort.Sort(sort.Reverse(p))
- }
- sorted := make([]interface{}, len(p.Pairs))
- for i, v := range p.Pairs {
- sorted[i] = v.Value.Interface()
- }
-
- return sorted
-}
-
-func IsSet(a interface{}, key interface{}) bool {
- av := reflect.ValueOf(a)
- kv := reflect.ValueOf(key)
-
- switch av.Kind() {
- case reflect.Array, reflect.Chan, reflect.Slice:
- if int64(av.Len()) > kv.Int() {
- return true
- }
- case reflect.Map:
- if kv.Type() == av.Type().Key() {
- return av.MapIndex(kv).IsValid()
- }
- }
-
- return false
-}
-
-func ReturnWhenSet(a, k interface{}) interface{} {
- av, isNil := indirect(reflect.ValueOf(a))
- if isNil {
- return ""
- }
-
- var avv reflect.Value
- switch av.Kind() {
- case reflect.Array, reflect.Slice:
- index, ok := k.(int)
- if ok && av.Len() > index {
- avv = av.Index(index)
- }
- case reflect.Map:
- kv := reflect.ValueOf(k)
- if kv.Type().AssignableTo(av.Type().Key()) {
- avv = av.MapIndex(kv)
- }
- }
-
- if avv.IsValid() {
- switch avv.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return avv.Int()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- return avv.Uint()
- case reflect.Float32, reflect.Float64:
- return avv.Float()
- case reflect.String:
- return avv.String()
- }
- }
-
- return ""
-}
-
-func Highlight(in interface{}, lang string) template.HTML {
- var str string
- av := reflect.ValueOf(in)
- switch av.Kind() {
- case reflect.String:
- str = av.String()
- }
-
- return template.HTML(helpers.Highlight(html.UnescapeString(str), lang))
-}
-
-var markdownTrimPrefix = []byte("<p>")
-var markdownTrimSuffix = []byte("</p>\n")
-
-func Markdownify(text string) template.HTML {
- m := helpers.RenderBytes(&helpers.RenderingContext{Content: []byte(text), PageFmt: "markdown"})
- m = bytes.TrimPrefix(m, markdownTrimPrefix)
- m = bytes.TrimSuffix(m, markdownTrimSuffix)
- return template.HTML(m)
-}
-
-func refPage(page interface{}, ref, methodName string) template.HTML {
- value := reflect.ValueOf(page)
-
- method := value.MethodByName(methodName)
-
- if method.IsValid() && method.Type().NumIn() == 1 && method.Type().NumOut() == 2 {
- result := method.Call([]reflect.Value{reflect.ValueOf(ref)})
-
- url, err := result[0], result[1]
-
- if !err.IsNil() {
- jww.ERROR.Printf("%s", err.Interface())
- return template.HTML(fmt.Sprintf("%s", err.Interface()))
- }
-
- if url.String() == "" {
- jww.ERROR.Printf("ref %s could not be found\n", ref)
- return template.HTML(ref)
- }
-
- return template.HTML(url.String())
- }
-
- jww.ERROR.Printf("Can only create references from Page and Node objects.")
- return template.HTML(ref)
-}
-
-func Ref(page interface{}, ref string) template.HTML {
- return refPage(page, ref, "Ref")
-}
-
-func RelRef(page interface{}, ref string) template.HTML {
- return refPage(page, ref, "RelRef")
-}
-
-func Chomp(text interface{}) (string, error) {
- s, err := cast.ToStringE(text)
- if err != nil {
- return "", err
- }
-
- return strings.TrimRight(s, "\r\n"), nil
-}
-
-// Trim leading/trailing characters defined by b from a
-func Trim(a interface{}, b string) (string, error) {
- aStr, err := cast.ToStringE(a)
- if err != nil {
- return "", err
- }
- return strings.Trim(aStr, b), nil
-}
-
-// Replace all occurences of b with c in a
-func Replace(a, b, c interface{}) (string, error) {
- aStr, err := cast.ToStringE(a)
- if err != nil {
- return "", err
- }
- bStr, err := cast.ToStringE(b)
- if err != nil {
- return "", err
- }
- cStr, err := cast.ToStringE(c)
- if err != nil {
- return "", err
- }
- return strings.Replace(aStr, bStr, cStr, -1), nil
-}
-
-// DateFormat converts the textual representation of the datetime string into
-// the other form or returns it of the time.Time value. These are formatted
-// with the layout string
-func DateFormat(layout string, v interface{}) (string, error) {
- t, err := cast.ToTimeE(v)
- if err != nil {
- return "", err
- }
- return t.Format(layout), nil
-}
-
-func SafeHTML(text string) template.HTML {
- return template.HTML(text)
-}
-
-// "safeHTMLAttr" is currently disabled, pending further discussion
-// on its use case. 2015-01-19
-func SafeHTMLAttr(text string) template.HTMLAttr {
- return template.HTMLAttr(text)
-}
-
-func SafeCSS(text string) template.CSS {
- return template.CSS(text)
-}
-
-func SafeURL(text string) template.URL {
- return template.URL(text)
-}
-
-func doArithmetic(a, b interface{}, op rune) (interface{}, error) {
- av := reflect.ValueOf(a)
- bv := reflect.ValueOf(b)
- var ai, bi int64
- var af, bf float64
- var au, bu uint64
- switch av.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- ai = av.Int()
- switch bv.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- bi = bv.Int()
- case reflect.Float32, reflect.Float64:
- af = float64(ai) // may overflow
- ai = 0
- bf = bv.Float()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- bu = bv.Uint()
- if ai >= 0 {
- au = uint64(ai)
- ai = 0
- } else {
- bi = int64(bu) // may overflow
- bu = 0
- }
- default:
- return nil, errors.New("Can't apply the operator to the values")
- }
- case reflect.Float32, reflect.Float64:
- af = av.Float()
- switch bv.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- bf = float64(bv.Int()) // may overflow
- case reflect.Float32, reflect.Float64:
- bf = bv.Float()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- bf = float64(bv.Uint()) // may overflow
- default:
- return nil, errors.New("Can't apply the operator to the values")
- }
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- au = av.Uint()
- switch bv.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- bi = bv.Int()
- if bi >= 0 {
- bu = uint64(bi)
- bi = 0
- } else {
- ai = int64(au) // may overflow
- au = 0
- }
- case reflect.Float32, reflect.Float64:
- af = float64(au) // may overflow
- au = 0
- bf = bv.Float()
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- bu = bv.Uint()
- default:
- return nil, errors.New("Can't apply the operator to the values")
- }
- case reflect.String:
- as := av.String()
- if bv.Kind() == reflect.String && op == '+' {
- bs := bv.String()
- return as + bs, nil
- } else {
- return nil, errors.New("Can't apply the operator to the values")
- }
- default:
- return nil, errors.New("Can't apply the operator to the values")
- }
-
- switch op {
- case '+':
- if ai != 0 || bi != 0 {
- return ai + bi, nil
- } else if af != 0 || bf != 0 {
- return af + bf, nil
- } else if au != 0 || bu != 0 {
- return au + bu, nil
- } else {
- return 0, nil
- }
- case '-':
- if ai != 0 || bi != 0 {
- return ai - bi, nil
- } else if af != 0 || bf != 0 {
- return af - bf, nil
- } else if au != 0 || bu != 0 {
- return au - bu, nil
- } else {
- return 0, nil
- }
- case '*':
- if ai != 0 || bi != 0 {
- return ai * bi, nil
- } else if af != 0 || bf != 0 {
- return af * bf, nil
- } else if au != 0 || bu != 0 {
- return au * bu, nil
- } else {
- return 0, nil
- }
- case '/':
- if bi != 0 {
- return ai / bi, nil
- } else if bf != 0 {
- return af / bf, nil
- } else if bu != 0 {
- return au / bu, nil
- } else {
- return nil, errors.New("Can't divide the value by 0")
- }
- default:
- return nil, errors.New("There is no such an operation")
- }
-}
-
-func Mod(a, b interface{}) (int64, error) {
- av := reflect.ValueOf(a)
- bv := reflect.ValueOf(b)
- var ai, bi int64
-
- switch av.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- ai = av.Int()
- default:
- return 0, errors.New("Modulo operator can't be used with non integer value")
- }
-
- switch bv.Kind() {
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- bi = bv.Int()
- default:
- return 0, errors.New("Modulo operator can't be used with non integer value")
- }
-
- if bi == 0 {
- return 0, errors.New("The number can't be divided by zero at modulo operation")
- }
-
- return ai % bi, nil
-}
-
-func ModBool(a, b interface{}) (bool, error) {
- res, err := Mod(a, b)
- if err != nil {
- return false, err
- }
- return res == int64(0), nil
-}
-
func Partial(name string, context_list ...interface{}) template.HTML {
if strings.HasPrefix("partials/", name) {
name = name[8:]
@@ -1351,8 +226,6 @@
return path[len(path)-1] == '~'
}
-// TODO(bep) split this file in two => template_funcs.go + tests.
-
const baseAceFilename = "baseof.ace"
var aceTemplateInnerMarker = []byte("= content")
@@ -1440,83 +313,4 @@
for _, e := range t.errors {
jww.ERROR.Println(e.err)
}
-}
-
-func init() {
- funcMap = template.FuncMap{
- "urlize": helpers.URLize,
- "sanitizeURL": helpers.SanitizeURL,
- "sanitizeurl": helpers.SanitizeURL,
- "eq": Eq,
- "ne": Ne,
- "gt": Gt,
- "ge": Ge,
- "lt": Lt,
- "le": Le,
- "in": In,
- "slicestr": Slicestr,
- "substr": Substr,
- "split": Split,
- "intersect": Intersect,
- "isSet": IsSet,
- "isset": IsSet,
- "echoParam": ReturnWhenSet,
- "safeHTML": SafeHTML,
- "safeCSS": SafeCSS,
- "safeURL": SafeURL,
- "markdownify": Markdownify,
- "first": First,
- "where": Where,
- "delimit": Delimit,
- "sort": Sort,
- "highlight": Highlight,
- "add": func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '+') },
- "sub": func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '-') },
- "div": func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '/') },
- "mod": Mod,
- "mul": func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '*') },
- "modBool": ModBool,
- "lower": func(a string) string { return strings.ToLower(a) },
- "upper": func(a string) string { return strings.ToUpper(a) },
- "title": func(a string) string { return strings.Title(a) },
- "partial": Partial,
- "ref": Ref,
- "relref": RelRef,
- "apply": Apply,
- "chomp": Chomp,
- "replace": Replace,
- "trim": Trim,
- "dateFormat": DateFormat,
- "getJSON": GetJSON,
- "getCSV": GetCSV,
- "seq": helpers.Seq,
- "getenv": func(varName string) string { return os.Getenv(varName) },
-
- // "getJson" is deprecated. Will be removed in 0.15.
- "getJson": func(urlParts ...string) interface{} {
- helpers.Deprecated("Template", "getJson", "getJSON")
- return GetJSON(urlParts...)
- },
- // "getJson" is deprecated. Will be removed in 0.15.
- "getCsv": func(sep string, urlParts ...string) [][]string {
- helpers.Deprecated("Template", "getCsv", "getCSV")
- return GetCSV(sep, urlParts...)
- },
- // "safeHtml" is deprecated. Will be removed in 0.15.
- "safeHtml": func(text string) template.HTML {
- helpers.Deprecated("Template", "safeHtml", "safeHTML")
- return SafeHTML(text)
- },
- // "safeCss" is deprecated. Will be removed in 0.15.
- "safeCss": func(text string) template.CSS {
- helpers.Deprecated("Template", "safeCss", "safeCSS")
- return SafeCSS(text)
- },
- // "safeUrl" is deprecated. Will be removed in 0.15.
- "safeUrl": func(text string) template.URL {
- helpers.Deprecated("Template", "safeUrl", "safeURL")
- return SafeURL(text)
- },
- }
-
}
--- /dev/null
+++ b/tpl/template_funcs.go
@@ -1,0 +1,1228 @@
+// Copyright © 2013-14 Steve Francia <[email protected]>.
+//
+// Licensed under the Simple Public License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://opensource.org/licenses/Simple-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package tpl
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "github.com/spf13/cast"
+ "github.com/spf13/hugo/helpers"
+ jww "github.com/spf13/jwalterweatherman"
+ "html"
+ "html/template"
+ "os"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+var funcMap template.FuncMap
+
+func Eq(x, y interface{}) bool {
+ normalize := func(v interface{}) interface{} {
+ vv := reflect.ValueOf(v)
+ switch vv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return vv.Int()
+ case reflect.Float32, reflect.Float64:
+ return vv.Float()
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return vv.Uint()
+ default:
+ return v
+ }
+ }
+ x = normalize(x)
+ y = normalize(y)
+ return reflect.DeepEqual(x, y)
+}
+
+func Ne(x, y interface{}) bool {
+ return !Eq(x, y)
+}
+
+func Ge(a, b interface{}) bool {
+ left, right := compareGetFloat(a, b)
+ return left >= right
+}
+
+func Gt(a, b interface{}) bool {
+ left, right := compareGetFloat(a, b)
+ return left > right
+}
+
+func Le(a, b interface{}) bool {
+ left, right := compareGetFloat(a, b)
+ return left <= right
+}
+
+func Lt(a, b interface{}) bool {
+ left, right := compareGetFloat(a, b)
+ return left < right
+}
+
+func compareGetFloat(a interface{}, b interface{}) (float64, float64) {
+ var left, right float64
+ var leftStr, rightStr *string
+ var err error
+ av := reflect.ValueOf(a)
+
+ switch av.Kind() {
+ case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
+ left = float64(av.Len())
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ left = float64(av.Int())
+ case reflect.Float32, reflect.Float64:
+ left = av.Float()
+ case reflect.String:
+ left, err = strconv.ParseFloat(av.String(), 64)
+ if err != nil {
+ str := av.String()
+ leftStr = &str
+ }
+ }
+
+ bv := reflect.ValueOf(b)
+
+ switch bv.Kind() {
+ case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
+ right = float64(bv.Len())
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ right = float64(bv.Int())
+ case reflect.Float32, reflect.Float64:
+ right = bv.Float()
+ case reflect.String:
+ right, err = strconv.ParseFloat(bv.String(), 64)
+ if err != nil {
+ str := bv.String()
+ rightStr = &str
+ }
+
+ }
+
+ switch {
+ case leftStr == nil || rightStr == nil:
+ case *leftStr < *rightStr:
+ return 0, 1
+ case *leftStr > *rightStr:
+ return 1, 0
+ default:
+ return 0, 0
+ }
+
+ return left, right
+}
+
+// Slicing in Slicestr is done by specifying a half-open range with
+// two indices, start and end. 1 and 4 creates a slice including elements 1 through 3.
+// The end index can be omitted, it defaults to the string's length.
+func Slicestr(a interface{}, startEnd ...int) (string, error) {
+ aStr, err := cast.ToStringE(a)
+ if err != nil {
+ return "", err
+ }
+
+ if len(startEnd) > 2 {
+ return "", errors.New("too many arguments")
+ }
+
+ if len(startEnd) == 2 {
+ return aStr[startEnd[0]:startEnd[1]], nil
+ } else if len(startEnd) == 1 {
+ return aStr[startEnd[0]:], nil
+ } else {
+ return aStr[:], nil
+ }
+
+}
+
+// Substr extracts parts of a string, beginning at the character at the specified
+// position, and returns the specified number of characters.
+//
+// It normally takes two parameters: start and length.
+// It can also take one parameter: start, i.e. length is omitted, in which case
+// the substring starting from start until the end of the string will be returned.
+//
+// To extract characters from the end of the string, use a negative start number.
+//
+// In addition, borrowing from the extended behavior described at http://php.net/substr,
+// if length is given and is negative, then that many characters will be omitted from
+// the end of string.
+func Substr(a interface{}, nums ...int) (string, error) {
+ aStr, err := cast.ToStringE(a)
+ if err != nil {
+ return "", err
+ }
+
+ var start, length int
+ switch len(nums) {
+ case 1:
+ start = nums[0]
+ length = len(aStr)
+ case 2:
+ start = nums[0]
+ length = nums[1]
+ default:
+ return "", errors.New("too many arguments")
+ }
+
+ if start < -len(aStr) {
+ start = 0
+ }
+ if start > len(aStr) {
+ return "", errors.New(fmt.Sprintf("start position out of bounds for %d-byte string", len(aStr)))
+ }
+
+ var s, e int
+ if start >= 0 && length >= 0 {
+ s = start
+ e = start + length
+ } else if start < 0 && length >= 0 {
+ s = len(aStr) + start - length + 1
+ e = len(aStr) + start + 1
+ } else if start >= 0 && length < 0 {
+ s = start
+ e = len(aStr) + length
+ } else {
+ s = len(aStr) + start
+ e = len(aStr) + length
+ }
+
+ if s > e {
+ return "", errors.New(fmt.Sprintf("calculated start position greater than end position: %d > %d", s, e))
+ }
+ if e > len(aStr) {
+ e = len(aStr)
+ }
+
+ return aStr[s:e], nil
+
+}
+
+func Split(a interface{}, delimiter string) ([]string, error) {
+ aStr, err := cast.ToStringE(a)
+ if err != nil {
+ return []string{}, err
+ }
+ return strings.Split(aStr, delimiter), nil
+}
+
+func Intersect(l1, l2 interface{}) (interface{}, error) {
+ if l1 == nil || l2 == nil {
+ return make([]interface{}, 0), nil
+ }
+
+ l1v := reflect.ValueOf(l1)
+ l2v := reflect.ValueOf(l2)
+
+ switch l1v.Kind() {
+ case reflect.Array, reflect.Slice:
+ switch l2v.Kind() {
+ case reflect.Array, reflect.Slice:
+ r := reflect.MakeSlice(l1v.Type(), 0, 0)
+ for i := 0; i < l1v.Len(); i++ {
+ l1vv := l1v.Index(i)
+ for j := 0; j < l2v.Len(); j++ {
+ l2vv := l2v.Index(j)
+ switch l1vv.Kind() {
+ case reflect.String:
+ if l1vv.Type() == l2vv.Type() && l1vv.String() == l2vv.String() && !In(r, l2vv) {
+ r = reflect.Append(r, l2vv)
+ }
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ switch l2vv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ if l1vv.Int() == l2vv.Int() && !In(r, l2vv) {
+ r = reflect.Append(r, l2vv)
+ }
+ }
+ case reflect.Float32, reflect.Float64:
+ switch l2vv.Kind() {
+ case reflect.Float32, reflect.Float64:
+ if l1vv.Float() == l2vv.Float() && !In(r, l2vv) {
+ r = reflect.Append(r, l2vv)
+ }
+ }
+ }
+ }
+ }
+ return r.Interface(), nil
+ default:
+ return nil, errors.New("can't iterate over " + reflect.ValueOf(l2).Type().String())
+ }
+ default:
+ return nil, errors.New("can't iterate over " + reflect.ValueOf(l1).Type().String())
+ }
+}
+
+func In(l interface{}, v interface{}) bool {
+ lv := reflect.ValueOf(l)
+ vv := reflect.ValueOf(v)
+
+ switch lv.Kind() {
+ case reflect.Array, reflect.Slice:
+ for i := 0; i < lv.Len(); i++ {
+ lvv := lv.Index(i)
+ switch lvv.Kind() {
+ case reflect.String:
+ if vv.Type() == lvv.Type() && vv.String() == lvv.String() {
+ return true
+ }
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ switch vv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ if vv.Int() == lvv.Int() {
+ return true
+ }
+ }
+ case reflect.Float32, reflect.Float64:
+ switch vv.Kind() {
+ case reflect.Float32, reflect.Float64:
+ if vv.Float() == lvv.Float() {
+ return true
+ }
+ }
+ }
+ }
+ case reflect.String:
+ if vv.Type() == lv.Type() && strings.Contains(lv.String(), vv.String()) {
+ return true
+ }
+ }
+ return false
+}
+
+// indirect is taken from 'text/template/exec.go'
+func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
+ for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() {
+ if v.IsNil() {
+ return v, true
+ }
+ if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
+ break
+ }
+ }
+ return v, false
+}
+
+// First is exposed to templates, to iterate over the first N items in a
+// rangeable list.
+func First(limit interface{}, seq interface{}) (interface{}, error) {
+
+ if limit == nil || seq == nil {
+ return nil, errors.New("both limit and seq must be provided")
+ }
+
+ limitv, err := cast.ToIntE(limit)
+
+ if err != nil {
+ return nil, err
+ }
+
+ if limitv < 1 {
+ return nil, errors.New("can't return negative/empty count of items from sequence")
+ }
+
+ seqv := reflect.ValueOf(seq)
+ seqv, isNil := indirect(seqv)
+ if isNil {
+ return nil, errors.New("can't iterate over a nil value")
+ }
+
+ switch seqv.Kind() {
+ case reflect.Array, reflect.Slice, reflect.String:
+ // okay
+ default:
+ return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
+ }
+ if limitv > seqv.Len() {
+ limitv = seqv.Len()
+ }
+ return seqv.Slice(0, limitv).Interface(), nil
+}
+
+var (
+ zero reflect.Value
+ errorType = reflect.TypeOf((*error)(nil)).Elem()
+)
+
+func evaluateSubElem(obj reflect.Value, elemName string) (reflect.Value, error) {
+ if !obj.IsValid() {
+ return zero, errors.New("can't evaluate an invalid value")
+ }
+ typ := obj.Type()
+ obj, isNil := indirect(obj)
+
+ // first, check whether obj has a method. In this case, obj is
+ // an interface, a struct or its pointer. If obj is a struct,
+ // to check all T and *T method, use obj pointer type Value
+ objPtr := obj
+ if objPtr.Kind() != reflect.Interface && objPtr.CanAddr() {
+ objPtr = objPtr.Addr()
+ }
+ mt, ok := objPtr.Type().MethodByName(elemName)
+ if ok {
+ if mt.PkgPath != "" {
+ return zero, fmt.Errorf("%s is an unexported method of type %s", elemName, typ)
+ }
+ // struct pointer has one receiver argument and interface doesn't have an argument
+ if mt.Type.NumIn() > 1 || mt.Type.NumOut() == 0 || mt.Type.NumOut() > 2 {
+ return zero, fmt.Errorf("%s is a method of type %s but doesn't satisfy requirements", elemName, typ)
+ }
+ if mt.Type.NumOut() == 1 && mt.Type.Out(0).Implements(errorType) {
+ return zero, fmt.Errorf("%s is a method of type %s but doesn't satisfy requirements", elemName, typ)
+ }
+ if mt.Type.NumOut() == 2 && !mt.Type.Out(1).Implements(errorType) {
+ return zero, fmt.Errorf("%s is a method of type %s but doesn't satisfy requirements", elemName, typ)
+ }
+ res := objPtr.Method(mt.Index).Call([]reflect.Value{})
+ if len(res) == 2 && !res[1].IsNil() {
+ return zero, fmt.Errorf("error at calling a method %s of type %s: %s", elemName, typ, res[1].Interface().(error))
+ }
+ return res[0], nil
+ }
+
+ // elemName isn't a method so next start to check whether it is
+ // a struct field or a map value. In both cases, it mustn't be
+ // a nil value
+ if isNil {
+ return zero, fmt.Errorf("can't evaluate a nil pointer of type %s by a struct field or map key name %s", typ, elemName)
+ }
+ switch obj.Kind() {
+ case reflect.Struct:
+ ft, ok := obj.Type().FieldByName(elemName)
+ if ok {
+ if ft.PkgPath != "" {
+ return zero, fmt.Errorf("%s is an unexported field of struct type %s", elemName, typ)
+ }
+ return obj.FieldByIndex(ft.Index), nil
+ }
+ return zero, fmt.Errorf("%s isn't a field of struct type %s", elemName, typ)
+ case reflect.Map:
+ kv := reflect.ValueOf(elemName)
+ if kv.Type().AssignableTo(obj.Type().Key()) {
+ return obj.MapIndex(kv), nil
+ }
+ return zero, fmt.Errorf("%s isn't a key of map type %s", elemName, typ)
+ }
+ return zero, fmt.Errorf("%s is neither a struct field, a method nor a map element of type %s", elemName, typ)
+}
+
+func checkCondition(v, mv reflect.Value, op string) (bool, error) {
+ if !v.IsValid() || !mv.IsValid() {
+ return false, nil
+ }
+
+ var isNil bool
+ v, isNil = indirect(v)
+ if isNil {
+ return false, nil
+ }
+ mv, isNil = indirect(mv)
+ if isNil {
+ return false, nil
+ }
+
+ var ivp, imvp *int64
+ var svp, smvp *string
+ var ima []int64
+ var sma []string
+ if mv.Type() == v.Type() {
+ switch v.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ iv := v.Int()
+ ivp = &iv
+ imv := mv.Int()
+ imvp = &imv
+ case reflect.String:
+ sv := v.String()
+ svp = &sv
+ smv := mv.String()
+ smvp = &smv
+ }
+ } else {
+ if mv.Kind() != reflect.Array && mv.Kind() != reflect.Slice {
+ return false, nil
+ }
+ if mv.Type().Elem() != v.Type() {
+ return false, nil
+ }
+ switch v.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ iv := v.Int()
+ ivp = &iv
+ for i := 0; i < mv.Len(); i++ {
+ ima = append(ima, mv.Index(i).Int())
+ }
+ case reflect.String:
+ sv := v.String()
+ svp = &sv
+ for i := 0; i < mv.Len(); i++ {
+ sma = append(sma, mv.Index(i).String())
+ }
+ }
+ }
+
+ switch op {
+ case "", "=", "==", "eq":
+ if ivp != nil && imvp != nil {
+ return *ivp == *imvp, nil
+ } else if svp != nil && smvp != nil {
+ return *svp == *smvp, nil
+ }
+ case "!=", "<>", "ne":
+ if ivp != nil && imvp != nil {
+ return *ivp != *imvp, nil
+ } else if svp != nil && smvp != nil {
+ return *svp != *smvp, nil
+ }
+ case ">=", "ge":
+ if ivp != nil && imvp != nil {
+ return *ivp >= *imvp, nil
+ } else if svp != nil && smvp != nil {
+ return *svp >= *smvp, nil
+ }
+ case ">", "gt":
+ if ivp != nil && imvp != nil {
+ return *ivp > *imvp, nil
+ } else if svp != nil && smvp != nil {
+ return *svp > *smvp, nil
+ }
+ case "<=", "le":
+ if ivp != nil && imvp != nil {
+ return *ivp <= *imvp, nil
+ } else if svp != nil && smvp != nil {
+ return *svp <= *smvp, nil
+ }
+ case "<", "lt":
+ if ivp != nil && imvp != nil {
+ return *ivp < *imvp, nil
+ } else if svp != nil && smvp != nil {
+ return *svp < *smvp, nil
+ }
+ case "in", "not in":
+ var r bool
+ if ivp != nil && len(ima) > 0 {
+ r = In(ima, *ivp)
+ } else if svp != nil {
+ if len(sma) > 0 {
+ r = In(sma, *svp)
+ } else if smvp != nil {
+ r = In(*smvp, *svp)
+ }
+ } else {
+ return false, nil
+ }
+ if op == "not in" {
+ return !r, nil
+ } else {
+ return r, nil
+ }
+ default:
+ return false, errors.New("no such an operator")
+ }
+ return false, nil
+}
+
+func Where(seq, key interface{}, args ...interface{}) (r interface{}, err error) {
+ seqv := reflect.ValueOf(seq)
+ kv := reflect.ValueOf(key)
+
+ var mv reflect.Value
+ var op string
+ switch len(args) {
+ case 1:
+ mv = reflect.ValueOf(args[0])
+ case 2:
+ var ok bool
+ if op, ok = args[0].(string); !ok {
+ return nil, errors.New("operator argument must be string type")
+ }
+ op = strings.TrimSpace(strings.ToLower(op))
+ mv = reflect.ValueOf(args[1])
+ default:
+ return nil, errors.New("can't evaluate the array by no match argument or more than or equal to two arguments")
+ }
+
+ seqv, isNil := indirect(seqv)
+ if isNil {
+ return nil, errors.New("can't iterate over a nil value of type " + reflect.ValueOf(seq).Type().String())
+ }
+
+ var path []string
+ if kv.Kind() == reflect.String {
+ path = strings.Split(strings.Trim(kv.String(), "."), ".")
+ }
+
+ switch seqv.Kind() {
+ case reflect.Array, reflect.Slice:
+ rv := reflect.MakeSlice(seqv.Type(), 0, 0)
+ for i := 0; i < seqv.Len(); i++ {
+ var vvv reflect.Value
+ rvv := seqv.Index(i)
+ if kv.Kind() == reflect.String {
+ vvv = rvv
+ for _, elemName := range path {
+ vvv, err = evaluateSubElem(vvv, elemName)
+ if err != nil {
+ return nil, err
+ }
+ }
+ } else {
+ vv, _ := indirect(rvv)
+ if vv.Kind() == reflect.Map && kv.Type().AssignableTo(vv.Type().Key()) {
+ vvv = vv.MapIndex(kv)
+ }
+ }
+ if ok, err := checkCondition(vvv, mv, op); ok {
+ rv = reflect.Append(rv, rvv)
+ } else if err != nil {
+ return nil, err
+ }
+ }
+ return rv.Interface(), nil
+ default:
+ return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
+ }
+}
+
+// Apply, given a map, array, or slice, returns a new slice with the function fname applied over it.
+func Apply(seq interface{}, fname string, args ...interface{}) (interface{}, error) {
+ if seq == nil {
+ return make([]interface{}, 0), nil
+ }
+
+ if fname == "apply" {
+ return nil, errors.New("can't apply myself (no turtles allowed)")
+ }
+
+ seqv := reflect.ValueOf(seq)
+ seqv, isNil := indirect(seqv)
+ if isNil {
+ return nil, errors.New("can't iterate over a nil value")
+ }
+
+ fn, found := funcMap[fname]
+ if !found {
+ return nil, errors.New("can't find function " + fname)
+ }
+
+ fnv := reflect.ValueOf(fn)
+
+ switch seqv.Kind() {
+ case reflect.Array, reflect.Slice:
+ r := make([]interface{}, seqv.Len())
+ for i := 0; i < seqv.Len(); i++ {
+ vv := seqv.Index(i)
+
+ vvv, err := applyFnToThis(fnv, vv, args...)
+
+ if err != nil {
+ return nil, err
+ }
+
+ r[i] = vvv.Interface()
+ }
+
+ return r, nil
+ default:
+ return nil, errors.New("can't apply over " + reflect.ValueOf(seq).Type().String())
+ }
+}
+
+func applyFnToThis(fn, this reflect.Value, args ...interface{}) (reflect.Value, error) {
+ n := make([]reflect.Value, len(args))
+ for i, arg := range args {
+ if arg == "." {
+ n[i] = this
+ } else {
+ n[i] = reflect.ValueOf(arg)
+ }
+ }
+
+ res := fn.Call(n)
+
+ if len(res) == 1 || res[1].IsNil() {
+ return res[0], nil
+ } else {
+ return reflect.ValueOf(nil), res[1].Interface().(error)
+ }
+}
+
+func Delimit(seq, delimiter interface{}, last ...interface{}) (template.HTML, error) {
+ d, err := cast.ToStringE(delimiter)
+ if err != nil {
+ return "", err
+ }
+
+ var dLast *string
+ for _, l := range last {
+ dStr, err := cast.ToStringE(l)
+ if err != nil {
+ dLast = nil
+ }
+ dLast = &dStr
+ break
+ }
+
+ seqv := reflect.ValueOf(seq)
+ seqv, isNil := indirect(seqv)
+ if isNil {
+ return "", errors.New("can't iterate over a nil value")
+ }
+
+ var str string
+ switch seqv.Kind() {
+ case reflect.Map:
+ sortSeq, err := Sort(seq)
+ if err != nil {
+ return "", err
+ }
+ seqv = reflect.ValueOf(sortSeq)
+ fallthrough
+ case reflect.Array, reflect.Slice, reflect.String:
+ for i := 0; i < seqv.Len(); i++ {
+ val := seqv.Index(i).Interface()
+ valStr, err := cast.ToStringE(val)
+ if err != nil {
+ continue
+ }
+ switch {
+ case i == seqv.Len()-2 && dLast != nil:
+ str += valStr + *dLast
+ case i == seqv.Len()-1:
+ str += valStr
+ default:
+ str += valStr + d
+ }
+ }
+
+ default:
+ return "", errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
+ }
+
+ return template.HTML(str), nil
+}
+
+func Sort(seq interface{}, args ...interface{}) ([]interface{}, error) {
+ seqv := reflect.ValueOf(seq)
+ seqv, isNil := indirect(seqv)
+ if isNil {
+ return nil, errors.New("can't iterate over a nil value")
+ }
+
+ // Create a list of pairs that will be used to do the sort
+ p := pairList{SortAsc: true}
+ p.Pairs = make([]pair, seqv.Len())
+
+ for i, l := range args {
+ dStr, err := cast.ToStringE(l)
+ switch {
+ case i == 0 && err != nil:
+ p.SortByField = ""
+ case i == 0 && err == nil:
+ p.SortByField = dStr
+ case i == 1 && err == nil && dStr == "desc":
+ p.SortAsc = false
+ case i == 1:
+ p.SortAsc = true
+ }
+ }
+
+ var sorted []interface{}
+ switch seqv.Kind() {
+ case reflect.Array, reflect.Slice:
+ for i := 0; i < seqv.Len(); i++ {
+ p.Pairs[i].Key = reflect.ValueOf(i)
+ p.Pairs[i].Value = seqv.Index(i)
+ }
+ if p.SortByField == "" {
+ p.SortByField = "value"
+ }
+
+ case reflect.Map:
+ keys := seqv.MapKeys()
+ for i := 0; i < seqv.Len(); i++ {
+ p.Pairs[i].Key = keys[i]
+ p.Pairs[i].Value = seqv.MapIndex(keys[i])
+ }
+
+ default:
+ return nil, errors.New("can't sort " + reflect.ValueOf(seq).Type().String())
+ }
+ sorted = p.sort()
+ return sorted, nil
+}
+
+// Credit for pair sorting method goes to Andrew Gerrand
+// https://groups.google.com/forum/#!topic/golang-nuts/FT7cjmcL7gw
+// A data structure to hold a key/value pair.
+type pair struct {
+ Key reflect.Value
+ Value reflect.Value
+}
+
+// A slice of pairs that implements sort.Interface to sort by Value.
+type pairList struct {
+ Pairs []pair
+ SortByField string
+ SortAsc bool
+}
+
+func (p pairList) Swap(i, j int) { p.Pairs[i], p.Pairs[j] = p.Pairs[j], p.Pairs[i] }
+func (p pairList) Len() int { return len(p.Pairs) }
+func (p pairList) Less(i, j int) bool {
+ var truth bool
+ switch {
+ case p.SortByField == "value":
+ iVal := p.Pairs[i].Value
+ jVal := p.Pairs[j].Value
+ truth = Lt(iVal.Interface(), jVal.Interface())
+
+ case p.SortByField != "":
+ if p.Pairs[i].Value.FieldByName(p.SortByField).IsValid() {
+ iVal := p.Pairs[i].Value.FieldByName(p.SortByField)
+ jVal := p.Pairs[j].Value.FieldByName(p.SortByField)
+ truth = Lt(iVal.Interface(), jVal.Interface())
+ }
+ default:
+ iVal := p.Pairs[i].Key
+ jVal := p.Pairs[j].Key
+ truth = Lt(iVal.Interface(), jVal.Interface())
+ }
+ return truth
+}
+
+// sorts a pairList and returns a slice of sorted values
+func (p pairList) sort() []interface{} {
+ if p.SortAsc {
+ sort.Sort(p)
+ } else {
+ sort.Sort(sort.Reverse(p))
+ }
+ sorted := make([]interface{}, len(p.Pairs))
+ for i, v := range p.Pairs {
+ sorted[i] = v.Value.Interface()
+ }
+
+ return sorted
+}
+
+func IsSet(a interface{}, key interface{}) bool {
+ av := reflect.ValueOf(a)
+ kv := reflect.ValueOf(key)
+
+ switch av.Kind() {
+ case reflect.Array, reflect.Chan, reflect.Slice:
+ if int64(av.Len()) > kv.Int() {
+ return true
+ }
+ case reflect.Map:
+ if kv.Type() == av.Type().Key() {
+ return av.MapIndex(kv).IsValid()
+ }
+ }
+
+ return false
+}
+
+func ReturnWhenSet(a, k interface{}) interface{} {
+ av, isNil := indirect(reflect.ValueOf(a))
+ if isNil {
+ return ""
+ }
+
+ var avv reflect.Value
+ switch av.Kind() {
+ case reflect.Array, reflect.Slice:
+ index, ok := k.(int)
+ if ok && av.Len() > index {
+ avv = av.Index(index)
+ }
+ case reflect.Map:
+ kv := reflect.ValueOf(k)
+ if kv.Type().AssignableTo(av.Type().Key()) {
+ avv = av.MapIndex(kv)
+ }
+ }
+
+ if avv.IsValid() {
+ switch avv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return avv.Int()
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return avv.Uint()
+ case reflect.Float32, reflect.Float64:
+ return avv.Float()
+ case reflect.String:
+ return avv.String()
+ }
+ }
+
+ return ""
+}
+
+func Highlight(in interface{}, lang string) template.HTML {
+ var str string
+ av := reflect.ValueOf(in)
+ switch av.Kind() {
+ case reflect.String:
+ str = av.String()
+ }
+
+ return template.HTML(helpers.Highlight(html.UnescapeString(str), lang))
+}
+
+var markdownTrimPrefix = []byte("<p>")
+var markdownTrimSuffix = []byte("</p>\n")
+
+func Markdownify(text string) template.HTML {
+ m := helpers.RenderBytes(&helpers.RenderingContext{Content: []byte(text), PageFmt: "markdown"})
+ m = bytes.TrimPrefix(m, markdownTrimPrefix)
+ m = bytes.TrimSuffix(m, markdownTrimSuffix)
+ return template.HTML(m)
+}
+
+func refPage(page interface{}, ref, methodName string) template.HTML {
+ value := reflect.ValueOf(page)
+
+ method := value.MethodByName(methodName)
+
+ if method.IsValid() && method.Type().NumIn() == 1 && method.Type().NumOut() == 2 {
+ result := method.Call([]reflect.Value{reflect.ValueOf(ref)})
+
+ url, err := result[0], result[1]
+
+ if !err.IsNil() {
+ jww.ERROR.Printf("%s", err.Interface())
+ return template.HTML(fmt.Sprintf("%s", err.Interface()))
+ }
+
+ if url.String() == "" {
+ jww.ERROR.Printf("ref %s could not be found\n", ref)
+ return template.HTML(ref)
+ }
+
+ return template.HTML(url.String())
+ }
+
+ jww.ERROR.Printf("Can only create references from Page and Node objects.")
+ return template.HTML(ref)
+}
+
+func Ref(page interface{}, ref string) template.HTML {
+ return refPage(page, ref, "Ref")
+}
+
+func RelRef(page interface{}, ref string) template.HTML {
+ return refPage(page, ref, "RelRef")
+}
+
+func Chomp(text interface{}) (string, error) {
+ s, err := cast.ToStringE(text)
+ if err != nil {
+ return "", err
+ }
+
+ return strings.TrimRight(s, "\r\n"), nil
+}
+
+// Trim leading/trailing characters defined by b from a
+func Trim(a interface{}, b string) (string, error) {
+ aStr, err := cast.ToStringE(a)
+ if err != nil {
+ return "", err
+ }
+ return strings.Trim(aStr, b), nil
+}
+
+// Replace all occurences of b with c in a
+func Replace(a, b, c interface{}) (string, error) {
+ aStr, err := cast.ToStringE(a)
+ if err != nil {
+ return "", err
+ }
+ bStr, err := cast.ToStringE(b)
+ if err != nil {
+ return "", err
+ }
+ cStr, err := cast.ToStringE(c)
+ if err != nil {
+ return "", err
+ }
+ return strings.Replace(aStr, bStr, cStr, -1), nil
+}
+
+// DateFormat converts the textual representation of the datetime string into
+// the other form or returns it of the time.Time value. These are formatted
+// with the layout string
+func DateFormat(layout string, v interface{}) (string, error) {
+ t, err := cast.ToTimeE(v)
+ if err != nil {
+ return "", err
+ }
+ return t.Format(layout), nil
+}
+
+func SafeHTML(text string) template.HTML {
+ return template.HTML(text)
+}
+
+// "safeHTMLAttr" is currently disabled, pending further discussion
+// on its use case. 2015-01-19
+func SafeHTMLAttr(text string) template.HTMLAttr {
+ return template.HTMLAttr(text)
+}
+
+func SafeCSS(text string) template.CSS {
+ return template.CSS(text)
+}
+
+func SafeURL(text string) template.URL {
+ return template.URL(text)
+}
+
+func doArithmetic(a, b interface{}, op rune) (interface{}, error) {
+ av := reflect.ValueOf(a)
+ bv := reflect.ValueOf(b)
+ var ai, bi int64
+ var af, bf float64
+ var au, bu uint64
+ switch av.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ ai = av.Int()
+ switch bv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ bi = bv.Int()
+ case reflect.Float32, reflect.Float64:
+ af = float64(ai) // may overflow
+ ai = 0
+ bf = bv.Float()
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ bu = bv.Uint()
+ if ai >= 0 {
+ au = uint64(ai)
+ ai = 0
+ } else {
+ bi = int64(bu) // may overflow
+ bu = 0
+ }
+ default:
+ return nil, errors.New("Can't apply the operator to the values")
+ }
+ case reflect.Float32, reflect.Float64:
+ af = av.Float()
+ switch bv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ bf = float64(bv.Int()) // may overflow
+ case reflect.Float32, reflect.Float64:
+ bf = bv.Float()
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ bf = float64(bv.Uint()) // may overflow
+ default:
+ return nil, errors.New("Can't apply the operator to the values")
+ }
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ au = av.Uint()
+ switch bv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ bi = bv.Int()
+ if bi >= 0 {
+ bu = uint64(bi)
+ bi = 0
+ } else {
+ ai = int64(au) // may overflow
+ au = 0
+ }
+ case reflect.Float32, reflect.Float64:
+ af = float64(au) // may overflow
+ au = 0
+ bf = bv.Float()
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ bu = bv.Uint()
+ default:
+ return nil, errors.New("Can't apply the operator to the values")
+ }
+ case reflect.String:
+ as := av.String()
+ if bv.Kind() == reflect.String && op == '+' {
+ bs := bv.String()
+ return as + bs, nil
+ } else {
+ return nil, errors.New("Can't apply the operator to the values")
+ }
+ default:
+ return nil, errors.New("Can't apply the operator to the values")
+ }
+
+ switch op {
+ case '+':
+ if ai != 0 || bi != 0 {
+ return ai + bi, nil
+ } else if af != 0 || bf != 0 {
+ return af + bf, nil
+ } else if au != 0 || bu != 0 {
+ return au + bu, nil
+ } else {
+ return 0, nil
+ }
+ case '-':
+ if ai != 0 || bi != 0 {
+ return ai - bi, nil
+ } else if af != 0 || bf != 0 {
+ return af - bf, nil
+ } else if au != 0 || bu != 0 {
+ return au - bu, nil
+ } else {
+ return 0, nil
+ }
+ case '*':
+ if ai != 0 || bi != 0 {
+ return ai * bi, nil
+ } else if af != 0 || bf != 0 {
+ return af * bf, nil
+ } else if au != 0 || bu != 0 {
+ return au * bu, nil
+ } else {
+ return 0, nil
+ }
+ case '/':
+ if bi != 0 {
+ return ai / bi, nil
+ } else if bf != 0 {
+ return af / bf, nil
+ } else if bu != 0 {
+ return au / bu, nil
+ } else {
+ return nil, errors.New("Can't divide the value by 0")
+ }
+ default:
+ return nil, errors.New("There is no such an operation")
+ }
+}
+
+func Mod(a, b interface{}) (int64, error) {
+ av := reflect.ValueOf(a)
+ bv := reflect.ValueOf(b)
+ var ai, bi int64
+
+ switch av.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ ai = av.Int()
+ default:
+ return 0, errors.New("Modulo operator can't be used with non integer value")
+ }
+
+ switch bv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ bi = bv.Int()
+ default:
+ return 0, errors.New("Modulo operator can't be used with non integer value")
+ }
+
+ if bi == 0 {
+ return 0, errors.New("The number can't be divided by zero at modulo operation")
+ }
+
+ return ai % bi, nil
+}
+
+func ModBool(a, b interface{}) (bool, error) {
+ res, err := Mod(a, b)
+ if err != nil {
+ return false, err
+ }
+ return res == int64(0), nil
+}
+
+func init() {
+ funcMap = template.FuncMap{
+ "urlize": helpers.URLize,
+ "sanitizeURL": helpers.SanitizeURL,
+ "sanitizeurl": helpers.SanitizeURL,
+ "eq": Eq,
+ "ne": Ne,
+ "gt": Gt,
+ "ge": Ge,
+ "lt": Lt,
+ "le": Le,
+ "in": In,
+ "slicestr": Slicestr,
+ "substr": Substr,
+ "split": Split,
+ "intersect": Intersect,
+ "isSet": IsSet,
+ "isset": IsSet,
+ "echoParam": ReturnWhenSet,
+ "safeHTML": SafeHTML,
+ "safeCSS": SafeCSS,
+ "safeURL": SafeURL,
+ "markdownify": Markdownify,
+ "first": First,
+ "where": Where,
+ "delimit": Delimit,
+ "sort": Sort,
+ "highlight": Highlight,
+ "add": func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '+') },
+ "sub": func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '-') },
+ "div": func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '/') },
+ "mod": Mod,
+ "mul": func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '*') },
+ "modBool": ModBool,
+ "lower": func(a string) string { return strings.ToLower(a) },
+ "upper": func(a string) string { return strings.ToUpper(a) },
+ "title": func(a string) string { return strings.Title(a) },
+ "partial": Partial,
+ "ref": Ref,
+ "relref": RelRef,
+ "apply": Apply,
+ "chomp": Chomp,
+ "replace": Replace,
+ "trim": Trim,
+ "dateFormat": DateFormat,
+ "getJSON": GetJSON,
+ "getCSV": GetCSV,
+ "seq": helpers.Seq,
+ "getenv": func(varName string) string { return os.Getenv(varName) },
+
+ // "getJson" is deprecated. Will be removed in 0.15.
+ "getJson": func(urlParts ...string) interface{} {
+ helpers.Deprecated("Template", "getJson", "getJSON")
+ return GetJSON(urlParts...)
+ },
+ // "getJson" is deprecated. Will be removed in 0.15.
+ "getCsv": func(sep string, urlParts ...string) [][]string {
+ helpers.Deprecated("Template", "getCsv", "getCSV")
+ return GetCSV(sep, urlParts...)
+ },
+ // "safeHtml" is deprecated. Will be removed in 0.15.
+ "safeHtml": func(text string) template.HTML {
+ helpers.Deprecated("Template", "safeHtml", "safeHTML")
+ return SafeHTML(text)
+ },
+ // "safeCss" is deprecated. Will be removed in 0.15.
+ "safeCss": func(text string) template.CSS {
+ helpers.Deprecated("Template", "safeCss", "safeCSS")
+ return SafeCSS(text)
+ },
+ // "safeUrl" is deprecated. Will be removed in 0.15.
+ "safeUrl": func(text string) template.URL {
+ helpers.Deprecated("Template", "safeUrl", "safeURL")
+ return SafeURL(text)
+ },
+ }
+
+}
--- /dev/null
+++ b/tpl/template_funcs_test.go
@@ -1,0 +1,1222 @@
+package tpl
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "github.com/stretchr/testify/assert"
+ "html/template"
+ "path"
+ "reflect"
+ "runtime"
+ "testing"
+ "time"
+)
+
+type tstNoStringer struct {
+}
+
+type tstCompareType int
+
+const (
+ tstEq tstCompareType = iota
+ tstNe
+ tstGt
+ tstGe
+ tstLt
+ tstLe
+)
+
+func tstIsEq(tp tstCompareType) bool {
+ return tp == tstEq || tp == tstGe || tp == tstLe
+}
+
+func tstIsGt(tp tstCompareType) bool {
+ return tp == tstGt || tp == tstGe
+}
+
+func tstIsLt(tp tstCompareType) bool {
+ return tp == tstLt || tp == tstLe
+}
+
+func TestCompare(t *testing.T) {
+ for _, this := range []struct {
+ tstCompareType
+ funcUnderTest func(a, b interface{}) bool
+ }{
+ {tstGt, Gt},
+ {tstLt, Lt},
+ {tstGe, Ge},
+ {tstLe, Le},
+ {tstEq, Eq},
+ {tstNe, Ne},
+ } {
+ doTestCompare(t, this.tstCompareType, this.funcUnderTest)
+ }
+
+}
+
+func doTestCompare(t *testing.T, tp tstCompareType, funcUnderTest func(a, b interface{}) bool) {
+ for i, this := range []struct {
+ left interface{}
+ right interface{}
+ expectIndicator int
+ }{
+ {5, 8, -1},
+ {8, 5, 1},
+ {5, 5, 0},
+ {int(5), int64(5), 0},
+ {int32(5), int(5), 0},
+ {int16(4), int(5), -1},
+ {uint(15), uint64(15), 0},
+ {-2, 1, -1},
+ {2, -5, 1},
+ {0.0, 1.23, -1},
+ {1.1, 1.1, 0},
+ {float32(1.0), float64(1.0), 0},
+ {1.23, 0.0, 1},
+ {"5", "5", 0},
+ {"8", "5", 1},
+ {"5", "0001", 1},
+ {[]int{100, 99}, []int{1, 2, 3, 4}, -1},
+ } {
+ result := funcUnderTest(this.left, this.right)
+ success := false
+
+ if this.expectIndicator == 0 {
+ if tstIsEq(tp) {
+ success = result
+ } else {
+ success = !result
+ }
+ }
+
+ if this.expectIndicator < 0 {
+ success = result && (tstIsLt(tp) || tp == tstNe)
+ success = success || (!result && !tstIsLt(tp))
+ }
+
+ if this.expectIndicator > 0 {
+ success = result && (tstIsGt(tp) || tp == tstNe)
+ success = success || (!result && (!tstIsGt(tp) || tp != tstNe))
+ }
+
+ if !success {
+ t.Errorf("[%d][%s] %v compared to %v: %t", i, path.Base(runtime.FuncForPC(reflect.ValueOf(funcUnderTest).Pointer()).Name()), this.left, this.right, result)
+ }
+ }
+}
+
+func TestArethmic(t *testing.T) {
+ for i, this := range []struct {
+ a interface{}
+ b interface{}
+ op rune
+ expect interface{}
+ }{
+ {1, 2, '+', int64(3)},
+ {1, 2, '-', int64(-1)},
+ {2, 2, '*', int64(4)},
+ {4, 2, '/', int64(2)},
+ {uint8(1), uint8(3), '+', uint64(4)},
+ {uint8(3), uint8(2), '-', uint64(1)},
+ {uint8(2), uint8(2), '*', uint64(4)},
+ {uint16(4), uint8(2), '/', uint64(2)},
+ {4, 2, '¤', false},
+ {4, 0, '/', false},
+ } {
+ // TODO(bep): Take precision into account.
+ result, err := doArithmetic(this.a, this.b, this.op)
+ if b, ok := this.expect.(bool); ok && !b {
+ if err == nil {
+ t.Errorf("[%d] doArethmic didn't return an expected error", i)
+ }
+ } else {
+ if err != nil {
+ t.Errorf("[%d] failed: %s", i, err)
+ continue
+ }
+ if !reflect.DeepEqual(result, this.expect) {
+ t.Errorf("[%d] doArethmic got %v (%T) but expected %v (%T)", i, result, result, this.expect, this.expect)
+ }
+ }
+ }
+}
+
+func TestMod(t *testing.T) {
+ for i, this := range []struct {
+ a interface{}
+ b interface{}
+ expect interface{}
+ }{
+ {3, 2, int64(1)},
+ {3, 1, int64(0)},
+ {3, 0, false},
+ {0, 3, int64(0)},
+ {3.1, 2, false},
+ {3, 2.1, false},
+ {3.1, 2.1, false},
+ {int8(3), int8(2), int64(1)},
+ {int16(3), int16(2), int64(1)},
+ {int32(3), int32(2), int64(1)},
+ {int64(3), int64(2), int64(1)},
+ } {
+ result, err := Mod(this.a, this.b)
+ if b, ok := this.expect.(bool); ok && !b {
+ if err == nil {
+ t.Errorf("[%d] modulo didn't return an expected error", i)
+ }
+ } else {
+ if err != nil {
+ t.Errorf("[%d] failed: %s", i, err)
+ continue
+ }
+ if !reflect.DeepEqual(result, this.expect) {
+ t.Errorf("[%d] modulo got %v but expected %v", i, result, this.expect)
+ }
+ }
+ }
+}
+
+func TestModBool(t *testing.T) {
+ for i, this := range []struct {
+ a interface{}
+ b interface{}
+ expect interface{}
+ }{
+ {3, 3, true},
+ {3, 2, false},
+ {3, 1, true},
+ {3, 0, nil},
+ {0, 3, true},
+ {3.1, 2, nil},
+ {3, 2.1, nil},
+ {3.1, 2.1, nil},
+ {int8(3), int8(3), true},
+ {int8(3), int8(2), false},
+ {int16(3), int16(3), true},
+ {int16(3), int16(2), false},
+ {int32(3), int32(3), true},
+ {int32(3), int32(2), false},
+ {int64(3), int64(3), true},
+ {int64(3), int64(2), false},
+ } {
+ result, err := ModBool(this.a, this.b)
+ if this.expect == nil {
+ if err == nil {
+ t.Errorf("[%d] modulo didn't return an expected error", i)
+ }
+ } else {
+ if err != nil {
+ t.Errorf("[%d] failed: %s", i, err)
+ continue
+ }
+ if !reflect.DeepEqual(result, this.expect) {
+ t.Errorf("[%d] modulo got %v but expected %v", i, result, this.expect)
+ }
+ }
+ }
+}
+
+func TestFirst(t *testing.T) {
+ for i, this := range []struct {
+ count interface{}
+ sequence interface{}
+ expect interface{}
+ }{
+ {int(2), []string{"a", "b", "c"}, []string{"a", "b"}},
+ {int32(3), []string{"a", "b"}, []string{"a", "b"}},
+ {int64(2), []int{100, 200, 300}, []int{100, 200}},
+ {100, []int{100, 200}, []int{100, 200}},
+ {"1", []int{100, 200, 300}, []int{100}},
+ {int64(-1), []int{100, 200, 300}, false},
+ {"noint", []int{100, 200, 300}, false},
+ {1, nil, false},
+ {nil, []int{100}, false},
+ {1, t, false},
+ } {
+ results, err := First(this.count, this.sequence)
+ if b, ok := this.expect.(bool); ok && !b {
+ if err == nil {
+ t.Errorf("[%d] First didn't return an expected error", i)
+ }
+ } else {
+ if err != nil {
+ t.Errorf("[%d] failed: %s", i, err)
+ continue
+ }
+ if !reflect.DeepEqual(results, this.expect) {
+ t.Errorf("[%d] First %d items, got %v but expected %v", i, this.count, results, this.expect)
+ }
+ }
+ }
+}
+
+func TestIn(t *testing.T) {
+ for i, this := range []struct {
+ v1 interface{}
+ v2 interface{}
+ expect bool
+ }{
+ {[]string{"a", "b", "c"}, "b", true},
+ {[]string{"a", "b", "c"}, "d", false},
+ {[]string{"a", "12", "c"}, 12, false},
+ {[]int{1, 2, 4}, 2, true},
+ {[]int{1, 2, 4}, 3, false},
+ {[]float64{1.23, 2.45, 4.67}, 1.23, true},
+ {[]float64{1.234567, 2.45, 4.67}, 1.234568, false},
+ {"this substring should be found", "substring", true},
+ {"this substring should not be found", "subseastring", false},
+ } {
+ result := In(this.v1, this.v2)
+
+ if result != this.expect {
+ t.Errorf("[%d] Got %v but expected %v", i, result, this.expect)
+ }
+ }
+}
+
+func TestSlicestr(t *testing.T) {
+ for i, this := range []struct {
+ v1 interface{}
+ v2 []int
+ expect interface{}
+ }{
+ {"abc", []int{1, 2}, "b"},
+ {"abc", []int{1, 3}, "bc"},
+ {"abc", []int{0, 1}, "a"},
+ {"abcdef", []int{}, "abcdef"},
+ {"abcdef", []int{0, 6}, "abcdef"},
+ {"abcdef", []int{0, 2}, "ab"},
+ {"abcdef", []int{2}, "cdef"},
+ {123, []int{1, 3}, "23"},
+ {123, []int{1, 2, 3}, false},
+ {tstNoStringer{}, []int{0, 1}, false},
+ } {
+ result, err := Slicestr(this.v1, this.v2...)
+
+ if b, ok := this.expect.(bool); ok && !b {
+ if err == nil {
+ t.Errorf("[%d] Slice didn't return an expected error", i)
+ }
+ } else {
+ if err != nil {
+ t.Errorf("[%d] failed: %s", i, err)
+ continue
+ }
+ if !reflect.DeepEqual(result, this.expect) {
+ t.Errorf("[%d] Got %s but expected %s", i, result, this.expect)
+ }
+ }
+ }
+}
+
+func TestSubstr(t *testing.T) {
+ for i, this := range []struct {
+ v1 interface{}
+ v2 int
+ v3 int
+ expect interface{}
+ }{
+ {"abc", 1, 2, "bc"},
+ {"abc", 0, 1, "a"},
+ {"abcdef", -1, 2, "ef"},
+ {"abcdef", -3, 3, "bcd"},
+ {"abcdef", 0, -1, "abcde"},
+ {"abcdef", 2, -1, "cde"},
+ {"abcdef", 4, -4, false},
+ {"abcdef", 7, 1, false},
+ {"abcdef", 1, 100, "bcdef"},
+ {"abcdef", -100, 3, "abc"},
+ {"abcdef", -3, -1, "de"},
+ {123, 1, 3, "23"},
+ {1.2e3, 0, 4, "1200"},
+ {tstNoStringer{}, 0, 1, false},
+ } {
+ result, err := Substr(this.v1, this.v2, this.v3)
+
+ if b, ok := this.expect.(bool); ok && !b {
+ if err == nil {
+ t.Errorf("[%d] Substr didn't return an expected error", i)
+ }
+ } else {
+ if err != nil {
+ t.Errorf("[%d] failed: %s", i, err)
+ continue
+ }
+ if !reflect.DeepEqual(result, this.expect) {
+ t.Errorf("[%d] Got %s but expected %s", i, result, this.expect)
+ }
+ }
+ }
+}
+
+func TestSplit(t *testing.T) {
+ for i, this := range []struct {
+ v1 interface{}
+ v2 string
+ expect interface{}
+ }{
+ {"a, b", ", ", []string{"a", "b"}},
+ {"a & b & c", " & ", []string{"a", "b", "c"}},
+ {"http://exmaple.com", "http://", []string{"", "exmaple.com"}},
+ {123, "2", []string{"1", "3"}},
+ {tstNoStringer{}, ",", false},
+ } {
+ result, err := Split(this.v1, this.v2)
+
+ if b, ok := this.expect.(bool); ok && !b {
+ if err == nil {
+ t.Errorf("[%d] Split didn't return an expected error", i)
+ }
+ } else {
+ if err != nil {
+ t.Errorf("[%d] failed: %s", i, err)
+ continue
+ }
+ if !reflect.DeepEqual(result, this.expect) {
+ t.Errorf("[%d] Got %s but expected %s", i, result, this.expect)
+ }
+ }
+ }
+
+}
+
+func TestIntersect(t *testing.T) {
+ for i, this := range []struct {
+ sequence1 interface{}
+ sequence2 interface{}
+ expect interface{}
+ }{
+ {[]string{"a", "b", "c"}, []string{"a", "b"}, []string{"a", "b"}},
+ {[]string{"a", "b"}, []string{"a", "b", "c"}, []string{"a", "b"}},
+ {[]string{"a", "b", "c"}, []string{"d", "e"}, []string{}},
+ {[]string{}, []string{}, []string{}},
+ {[]string{"a", "b"}, nil, make([]interface{}, 0)},
+ {nil, []string{"a", "b"}, make([]interface{}, 0)},
+ {nil, nil, make([]interface{}, 0)},
+ {[]string{"1", "2"}, []int{1, 2}, []string{}},
+ {[]int{1, 2}, []string{"1", "2"}, []int{}},
+ {[]int{1, 2, 4}, []int{2, 4}, []int{2, 4}},
+ {[]int{2, 4}, []int{1, 2, 4}, []int{2, 4}},
+ {[]int{1, 2, 4}, []int{3, 6}, []int{}},
+ {[]float64{2.2, 4.4}, []float64{1.1, 2.2, 4.4}, []float64{2.2, 4.4}},
+ } {
+ results, err := Intersect(this.sequence1, this.sequence2)
+ if err != nil {
+ t.Errorf("[%d] failed: %s", i, err)
+ continue
+ }
+ if !reflect.DeepEqual(results, this.expect) {
+ t.Errorf("[%d] Got %v but expected %v", i, results, this.expect)
+ }
+ }
+
+ _, err1 := Intersect("not an array or slice", []string{"a"})
+
+ if err1 == nil {
+ t.Error("Excpected error for non array as first arg")
+ }
+
+ _, err2 := Intersect([]string{"a"}, "not an array or slice")
+
+ if err2 == nil {
+ t.Error("Excpected error for non array as second arg")
+ }
+}
+
+func TestIsSet(t *testing.T) {
+ aSlice := []interface{}{1, 2, 3, 5}
+ aMap := map[string]interface{}{"a": 1, "b": 2}
+
+ assert.True(t, IsSet(aSlice, 2))
+ assert.True(t, IsSet(aMap, "b"))
+ assert.False(t, IsSet(aSlice, 22))
+ assert.False(t, IsSet(aMap, "bc"))
+}
+
+func (x *TstX) TstRp() string {
+ return "r" + x.A
+}
+
+func (x TstX) TstRv() string {
+ return "r" + x.B
+}
+
+func (x TstX) unexportedMethod() string {
+ return x.unexported
+}
+
+func (x TstX) MethodWithArg(s string) string {
+ return s
+}
+
+func (x TstX) MethodReturnNothing() {}
+
+func (x TstX) MethodReturnErrorOnly() error {
+ return errors.New("something error occured")
+}
+
+func (x TstX) MethodReturnTwoValues() (string, string) {
+ return "foo", "bar"
+}
+
+func (x TstX) MethodReturnValueWithError() (string, error) {
+ return "", errors.New("something error occured")
+}
+
+func (x TstX) String() string {
+ return fmt.Sprintf("A: %s, B: %s", x.A, x.B)
+}
+
+type TstX struct {
+ A, B string
+ unexported string
+}
+
+func TestEvaluateSubElem(t *testing.T) {
+ tstx := TstX{A: "foo", B: "bar"}
+ var inner struct {
+ S fmt.Stringer
+ }
+ inner.S = tstx
+ interfaceValue := reflect.ValueOf(&inner).Elem().Field(0)
+
+ for i, this := range []struct {
+ value reflect.Value
+ key string
+ expect interface{}
+ }{
+ {reflect.ValueOf(tstx), "A", "foo"},
+ {reflect.ValueOf(&tstx), "TstRp", "rfoo"},
+ {reflect.ValueOf(tstx), "TstRv", "rbar"},
+ //{reflect.ValueOf(map[int]string{1: "foo", 2: "bar"}), 1, "foo"},
+ {reflect.ValueOf(map[string]string{"key1": "foo", "key2": "bar"}), "key1", "foo"},
+ {interfaceValue, "String", "A: foo, B: bar"},
+ {reflect.Value{}, "foo", false},
+ //{reflect.ValueOf(map[int]string{1: "foo", 2: "bar"}), 1.2, false},
+ {reflect.ValueOf(tstx), "unexported", false},
+ {reflect.ValueOf(tstx), "unexportedMethod", false},
+ {reflect.ValueOf(tstx), "MethodWithArg", false},
+ {reflect.ValueOf(tstx), "MethodReturnNothing", false},
+ {reflect.ValueOf(tstx), "MethodReturnErrorOnly", false},
+ {reflect.ValueOf(tstx), "MethodReturnTwoValues", false},
+ {reflect.ValueOf(tstx), "MethodReturnValueWithError", false},
+ {reflect.ValueOf((*TstX)(nil)), "A", false},
+ {reflect.ValueOf(tstx), "C", false},
+ {reflect.ValueOf(map[int]string{1: "foo", 2: "bar"}), "1", false},
+ {reflect.ValueOf([]string{"foo", "bar"}), "1", false},
+ } {
+ result, err := evaluateSubElem(this.value, this.key)
+ if b, ok := this.expect.(bool); ok && !b {
+ if err == nil {
+ t.Errorf("[%d] evaluateSubElem didn't return an expected error", i)
+ }
+ } else {
+ if err != nil {
+ t.Errorf("[%d] failed: %s", i, err)
+ continue
+ }
+ if result.Kind() != reflect.String || result.String() != this.expect {
+ t.Errorf("[%d] evaluateSubElem with %v got %v but expected %v", i, this.key, result, this.expect)
+ }
+ }
+ }
+}
+
+func TestCheckCondition(t *testing.T) {
+ type expect struct {
+ result bool
+ isError bool
+ }
+
+ for i, this := range []struct {
+ value reflect.Value
+ match reflect.Value
+ op string
+ expect
+ }{
+ {reflect.ValueOf(123), reflect.ValueOf(123), "", expect{true, false}},
+ {reflect.ValueOf("foo"), reflect.ValueOf("foo"), "", expect{true, false}},
+ {reflect.ValueOf(123), reflect.ValueOf(456), "!=", expect{true, false}},
+ {reflect.ValueOf("foo"), reflect.ValueOf("bar"), "!=", expect{true, false}},
+ {reflect.ValueOf(456), reflect.ValueOf(123), ">=", expect{true, false}},
+ {reflect.ValueOf("foo"), reflect.ValueOf("bar"), ">=", expect{true, false}},
+ {reflect.ValueOf(456), reflect.ValueOf(123), ">", expect{true, false}},
+ {reflect.ValueOf("foo"), reflect.ValueOf("bar"), ">", expect{true, false}},
+ {reflect.ValueOf(123), reflect.ValueOf(456), "<=", expect{true, false}},
+ {reflect.ValueOf("bar"), reflect.ValueOf("foo"), "<=", expect{true, false}},
+ {reflect.ValueOf(123), reflect.ValueOf(456), "<", expect{true, false}},
+ {reflect.ValueOf("bar"), reflect.ValueOf("foo"), "<", expect{true, false}},
+ {reflect.ValueOf(123), reflect.ValueOf([]int{123, 45, 678}), "in", expect{true, false}},
+ {reflect.ValueOf("foo"), reflect.ValueOf([]string{"foo", "bar", "baz"}), "in", expect{true, false}},
+ {reflect.ValueOf(123), reflect.ValueOf([]int{45, 678}), "not in", expect{true, false}},
+ {reflect.ValueOf("foo"), reflect.ValueOf([]string{"bar", "baz"}), "not in", expect{true, false}},
+ {reflect.ValueOf("foo"), reflect.ValueOf("bar-foo-baz"), "in", expect{true, false}},
+ {reflect.ValueOf("foo"), reflect.ValueOf("bar--baz"), "not in", expect{true, false}},
+ {reflect.Value{}, reflect.ValueOf("foo"), "", expect{false, false}},
+ {reflect.ValueOf("foo"), reflect.Value{}, "", expect{false, false}},
+ {reflect.ValueOf((*TstX)(nil)), reflect.ValueOf("foo"), "", expect{false, false}},
+ {reflect.ValueOf("foo"), reflect.ValueOf((*TstX)(nil)), "", expect{false, false}},
+ {reflect.ValueOf("foo"), reflect.ValueOf(map[int]string{}), "", expect{false, false}},
+ {reflect.ValueOf("foo"), reflect.ValueOf([]int{1, 2}), "", expect{false, false}},
+ {reflect.ValueOf(123), reflect.ValueOf([]int{}), "in", expect{false, false}},
+ {reflect.ValueOf(123), reflect.ValueOf(123), "op", expect{false, true}},
+ } {
+ result, err := checkCondition(this.value, this.match, this.op)
+ if this.expect.isError {
+ if err == nil {
+ t.Errorf("[%d] checkCondition didn't return an expected error", i)
+ }
+ } else {
+ if err != nil {
+ t.Errorf("[%d] failed: %s", i, err)
+ continue
+ }
+ if result != this.expect.result {
+ t.Errorf("[%d] check condition %v %s %v, got %v but expected %v", i, this.value, this.op, this.match, result, this.expect.result)
+ }
+ }
+ }
+}
+
+func TestWhere(t *testing.T) {
+ // TODO(spf): Put these page tests back in
+ //page1 := &Page{contentType: "v", Source: Source{File: *source.NewFile("/x/y/z/source.md")}}
+ //page2 := &Page{contentType: "w", Source: Source{File: *source.NewFile("/y/z/a/source.md")}}
+
+ type Mid struct {
+ Tst TstX
+ }
+
+ for i, this := range []struct {
+ sequence interface{}
+ key interface{}
+ op string
+ match interface{}
+ expect interface{}
+ }{
+ {
+ sequence: []map[int]string{
+ {1: "a", 2: "m"}, {1: "c", 2: "d"}, {1: "e", 3: "m"},
+ },
+ key: 2, match: "m",
+ expect: []map[int]string{
+ {1: "a", 2: "m"},
+ },
+ },
+ {
+ sequence: []map[string]int{
+ {"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "x": 4},
+ },
+ key: "b", match: 4,
+ expect: []map[string]int{
+ {"a": 3, "b": 4},
+ },
+ },
+ {
+ sequence: []TstX{
+ {A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"},
+ },
+ key: "B", match: "f",
+ expect: []TstX{
+ {A: "e", B: "f"},
+ },
+ },
+ {
+ sequence: []*map[int]string{
+ {1: "a", 2: "m"}, {1: "c", 2: "d"}, {1: "e", 3: "m"},
+ },
+ key: 2, match: "m",
+ expect: []*map[int]string{
+ {1: "a", 2: "m"},
+ },
+ },
+ {
+ sequence: []*TstX{
+ {A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"},
+ },
+ key: "B", match: "f",
+ expect: []*TstX{
+ {A: "e", B: "f"},
+ },
+ },
+ {
+ sequence: []*TstX{
+ {A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "c"},
+ },
+ key: "TstRp", match: "rc",
+ expect: []*TstX{
+ {A: "c", B: "d"},
+ },
+ },
+ {
+ sequence: []TstX{
+ {A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "c"},
+ },
+ key: "TstRv", match: "rc",
+ expect: []TstX{
+ {A: "e", B: "c"},
+ },
+ },
+ {
+ sequence: []map[string]TstX{
+ {"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}},
+ },
+ key: "foo.B", match: "d",
+ expect: []map[string]TstX{
+ {"foo": TstX{A: "c", B: "d"}},
+ },
+ },
+ {
+ sequence: []map[string]TstX{
+ {"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}},
+ },
+ key: ".foo.B", match: "d",
+ expect: []map[string]TstX{
+ {"foo": TstX{A: "c", B: "d"}},
+ },
+ },
+ {
+ sequence: []map[string]TstX{
+ {"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}},
+ },
+ key: "foo.TstRv", match: "rd",
+ expect: []map[string]TstX{
+ {"foo": TstX{A: "c", B: "d"}},
+ },
+ },
+ {
+ sequence: []map[string]*TstX{
+ {"foo": &TstX{A: "a", B: "b"}}, {"foo": &TstX{A: "c", B: "d"}}, {"foo": &TstX{A: "e", B: "f"}},
+ },
+ key: "foo.TstRp", match: "rc",
+ expect: []map[string]*TstX{
+ {"foo": &TstX{A: "c", B: "d"}},
+ },
+ },
+ {
+ sequence: []map[string]Mid{
+ {"foo": Mid{Tst: TstX{A: "a", B: "b"}}}, {"foo": Mid{Tst: TstX{A: "c", B: "d"}}}, {"foo": Mid{Tst: TstX{A: "e", B: "f"}}},
+ },
+ key: "foo.Tst.B", match: "d",
+ expect: []map[string]Mid{
+ {"foo": Mid{Tst: TstX{A: "c", B: "d"}}},
+ },
+ },
+ {
+ sequence: []map[string]Mid{
+ {"foo": Mid{Tst: TstX{A: "a", B: "b"}}}, {"foo": Mid{Tst: TstX{A: "c", B: "d"}}}, {"foo": Mid{Tst: TstX{A: "e", B: "f"}}},
+ },
+ key: "foo.Tst.TstRv", match: "rd",
+ expect: []map[string]Mid{
+ {"foo": Mid{Tst: TstX{A: "c", B: "d"}}},
+ },
+ },
+ {
+ sequence: []map[string]*Mid{
+ {"foo": &Mid{Tst: TstX{A: "a", B: "b"}}}, {"foo": &Mid{Tst: TstX{A: "c", B: "d"}}}, {"foo": &Mid{Tst: TstX{A: "e", B: "f"}}},
+ },
+ key: "foo.Tst.TstRp", match: "rc",
+ expect: []map[string]*Mid{
+ {"foo": &Mid{Tst: TstX{A: "c", B: "d"}}},
+ },
+ },
+ {
+ sequence: []map[string]int{
+ {"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "b": 6},
+ },
+ key: "b", op: ">", match: 3,
+ expect: []map[string]int{
+ {"a": 3, "b": 4}, {"a": 5, "b": 6},
+ },
+ },
+ {
+ sequence: []TstX{
+ {A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"},
+ },
+ key: "B", op: "!=", match: "f",
+ expect: []TstX{
+ {A: "a", B: "b"}, {A: "c", B: "d"},
+ },
+ },
+ {
+ sequence: []map[string]int{
+ {"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "b": 6},
+ },
+ key: "b", op: "in", match: []int{3, 4, 5},
+ expect: []map[string]int{
+ {"a": 3, "b": 4},
+ },
+ },
+ {
+ sequence: []TstX{
+ {A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"},
+ },
+ key: "B", op: "not in", match: []string{"c", "d", "e"},
+ expect: []TstX{
+ {A: "a", B: "b"}, {A: "e", B: "f"},
+ },
+ },
+ {sequence: (*[]TstX)(nil), key: "A", match: "a", expect: false},
+ {sequence: TstX{A: "a", B: "b"}, key: "A", match: "a", expect: false},
+ {sequence: []map[string]*TstX{{"foo": nil}}, key: "foo.B", match: "d", expect: false},
+ {
+ sequence: []TstX{
+ {A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"},
+ },
+ key: "B", op: "op", match: "f",
+ expect: false,
+ },
+ //{[]*Page{page1, page2}, "Type", "v", []*Page{page1}},
+ //{[]*Page{page1, page2}, "Section", "y", []*Page{page2}},
+ } {
+ var results interface{}
+ var err error
+ if len(this.op) > 0 {
+ results, err = Where(this.sequence, this.key, this.op, this.match)
+ } else {
+ results, err = Where(this.sequence, this.key, this.match)
+ }
+ if b, ok := this.expect.(bool); ok && !b {
+ if err == nil {
+ t.Errorf("[%d] Where didn't return an expected error", i)
+ }
+ } else {
+ if err != nil {
+ t.Errorf("[%d] failed: %s", i, err)
+ continue
+ }
+ if !reflect.DeepEqual(results, this.expect) {
+ t.Errorf("[%d] Where clause matching %v with %v, got %v but expected %v", i, this.key, this.match, results, this.expect)
+ }
+ }
+ }
+
+ var err error
+ _, err = Where(map[string]int{"a": 1, "b": 2}, "a", []byte("="), 1)
+ if err == nil {
+ t.Errorf("Where called with none string op value didn't return an expected error")
+ }
+
+ _, err = Where(map[string]int{"a": 1, "b": 2}, "a", []byte("="), 1, 2)
+ if err == nil {
+ t.Errorf("Where called with more than two variable arguments didn't return an expected error")
+ }
+
+ _, err = Where(map[string]int{"a": 1, "b": 2}, "a")
+ if err == nil {
+ t.Errorf("Where called with no variable arguments didn't return an expected error")
+ }
+}
+
+func TestDelimit(t *testing.T) {
+ for i, this := range []struct {
+ sequence interface{}
+ delimiter interface{}
+ last interface{}
+ expect template.HTML
+ }{
+ {[]string{"class1", "class2", "class3"}, " ", nil, "class1 class2 class3"},
+ {[]int{1, 2, 3, 4, 5}, ",", nil, "1,2,3,4,5"},
+ {[]int{1, 2, 3, 4, 5}, ", ", nil, "1, 2, 3, 4, 5"},
+ {[]string{"class1", "class2", "class3"}, " ", " and ", "class1 class2 and class3"},
+ {[]int{1, 2, 3, 4, 5}, ",", ",", "1,2,3,4,5"},
+ {[]int{1, 2, 3, 4, 5}, ", ", ", and ", "1, 2, 3, 4, and 5"},
+ // test maps with and without sorting required
+ {map[string]int{"1": 10, "2": 20, "3": 30, "4": 40, "5": 50}, "--", nil, "10--20--30--40--50"},
+ {map[string]int{"3": 10, "2": 20, "1": 30, "4": 40, "5": 50}, "--", nil, "30--20--10--40--50"},
+ {map[string]string{"1": "10", "2": "20", "3": "30", "4": "40", "5": "50"}, "--", nil, "10--20--30--40--50"},
+ {map[string]string{"3": "10", "2": "20", "1": "30", "4": "40", "5": "50"}, "--", nil, "30--20--10--40--50"},
+ {map[string]string{"one": "10", "two": "20", "three": "30", "four": "40", "five": "50"}, "--", nil, "50--40--10--30--20"},
+ {map[int]string{1: "10", 2: "20", 3: "30", 4: "40", 5: "50"}, "--", nil, "10--20--30--40--50"},
+ {map[int]string{3: "10", 2: "20", 1: "30", 4: "40", 5: "50"}, "--", nil, "30--20--10--40--50"},
+ {map[float64]string{3.3: "10", 2.3: "20", 1.3: "30", 4.3: "40", 5.3: "50"}, "--", nil, "30--20--10--40--50"},
+ // test maps with a last delimiter
+ {map[string]int{"1": 10, "2": 20, "3": 30, "4": 40, "5": 50}, "--", "--and--", "10--20--30--40--and--50"},
+ {map[string]int{"3": 10, "2": 20, "1": 30, "4": 40, "5": 50}, "--", "--and--", "30--20--10--40--and--50"},
+ {map[string]string{"1": "10", "2": "20", "3": "30", "4": "40", "5": "50"}, "--", "--and--", "10--20--30--40--and--50"},
+ {map[string]string{"3": "10", "2": "20", "1": "30", "4": "40", "5": "50"}, "--", "--and--", "30--20--10--40--and--50"},
+ {map[string]string{"one": "10", "two": "20", "three": "30", "four": "40", "five": "50"}, "--", "--and--", "50--40--10--30--and--20"},
+ {map[int]string{1: "10", 2: "20", 3: "30", 4: "40", 5: "50"}, "--", "--and--", "10--20--30--40--and--50"},
+ {map[int]string{3: "10", 2: "20", 1: "30", 4: "40", 5: "50"}, "--", "--and--", "30--20--10--40--and--50"},
+ {map[float64]string{3.5: "10", 2.5: "20", 1.5: "30", 4.5: "40", 5.5: "50"}, "--", "--and--", "30--20--10--40--and--50"},
+ } {
+ var result template.HTML
+ var err error
+ if this.last == nil {
+ result, err = Delimit(this.sequence, this.delimiter)
+ } else {
+ result, err = Delimit(this.sequence, this.delimiter, this.last)
+ }
+ if err != nil {
+ t.Errorf("[%d] failed: %s", i, err)
+ continue
+ }
+ if !reflect.DeepEqual(result, this.expect) {
+ t.Errorf("[%d] Delimit called on sequence: %v | delimiter: `%v` | last: `%v`, got %v but expected %v", i, this.sequence, this.delimiter, this.last, result, this.expect)
+ }
+ }
+}
+
+func TestSort(t *testing.T) {
+ type ts struct {
+ MyInt int
+ MyFloat float64
+ MyString string
+ }
+ for i, this := range []struct {
+ sequence interface{}
+ sortByField interface{}
+ sortAsc string
+ expect []interface{}
+ }{
+ {[]string{"class1", "class2", "class3"}, nil, "asc", []interface{}{"class1", "class2", "class3"}},
+ {[]string{"class3", "class1", "class2"}, nil, "asc", []interface{}{"class1", "class2", "class3"}},
+ {[]int{1, 2, 3, 4, 5}, nil, "asc", []interface{}{1, 2, 3, 4, 5}},
+ {[]int{5, 4, 3, 1, 2}, nil, "asc", []interface{}{1, 2, 3, 4, 5}},
+ // test map sorting by keys
+ {map[string]int{"1": 10, "2": 20, "3": 30, "4": 40, "5": 50}, nil, "asc", []interface{}{10, 20, 30, 40, 50}},
+ {map[string]int{"3": 10, "2": 20, "1": 30, "4": 40, "5": 50}, nil, "asc", []interface{}{30, 20, 10, 40, 50}},
+ {map[string]string{"1": "10", "2": "20", "3": "30", "4": "40", "5": "50"}, nil, "asc", []interface{}{"10", "20", "30", "40", "50"}},
+ {map[string]string{"3": "10", "2": "20", "1": "30", "4": "40", "5": "50"}, nil, "asc", []interface{}{"30", "20", "10", "40", "50"}},
+ {map[string]string{"one": "10", "two": "20", "three": "30", "four": "40", "five": "50"}, nil, "asc", []interface{}{"50", "40", "10", "30", "20"}},
+ {map[int]string{1: "10", 2: "20", 3: "30", 4: "40", 5: "50"}, nil, "asc", []interface{}{"10", "20", "30", "40", "50"}},
+ {map[int]string{3: "10", 2: "20", 1: "30", 4: "40", 5: "50"}, nil, "asc", []interface{}{"30", "20", "10", "40", "50"}},
+ {map[float64]string{3.3: "10", 2.3: "20", 1.3: "30", 4.3: "40", 5.3: "50"}, nil, "asc", []interface{}{"30", "20", "10", "40", "50"}},
+ // test map sorting by value
+ {map[string]int{"1": 10, "2": 20, "3": 30, "4": 40, "5": 50}, "value", "asc", []interface{}{10, 20, 30, 40, 50}},
+ {map[string]int{"3": 10, "2": 20, "1": 30, "4": 40, "5": 50}, "value", "asc", []interface{}{10, 20, 30, 40, 50}},
+ // test map sorting by field value
+ {
+ map[string]ts{"1": {10, 10.5, "ten"}, "2": {20, 20.5, "twenty"}, "3": {30, 30.5, "thirty"}, "4": {40, 40.5, "forty"}, "5": {50, 50.5, "fifty"}},
+ "MyInt",
+ "asc",
+ []interface{}{ts{10, 10.5, "ten"}, ts{20, 20.5, "twenty"}, ts{30, 30.5, "thirty"}, ts{40, 40.5, "forty"}, ts{50, 50.5, "fifty"}},
+ },
+ {
+ map[string]ts{"1": {10, 10.5, "ten"}, "2": {20, 20.5, "twenty"}, "3": {30, 30.5, "thirty"}, "4": {40, 40.5, "forty"}, "5": {50, 50.5, "fifty"}},
+ "MyFloat",
+ "asc",
+ []interface{}{ts{10, 10.5, "ten"}, ts{20, 20.5, "twenty"}, ts{30, 30.5, "thirty"}, ts{40, 40.5, "forty"}, ts{50, 50.5, "fifty"}},
+ },
+ {
+ map[string]ts{"1": {10, 10.5, "ten"}, "2": {20, 20.5, "twenty"}, "3": {30, 30.5, "thirty"}, "4": {40, 40.5, "forty"}, "5": {50, 50.5, "fifty"}},
+ "MyString",
+ "asc",
+ []interface{}{ts{50, 50.5, "fifty"}, ts{40, 40.5, "forty"}, ts{10, 10.5, "ten"}, ts{30, 30.5, "thirty"}, ts{20, 20.5, "twenty"}},
+ },
+ // Test sort desc
+ {[]string{"class1", "class2", "class3"}, "value", "desc", []interface{}{"class3", "class2", "class1"}},
+ {[]string{"class3", "class1", "class2"}, "value", "desc", []interface{}{"class3", "class2", "class1"}},
+ } {
+ var result []interface{}
+ var err error
+ if this.sortByField == nil {
+ result, err = Sort(this.sequence)
+ } else {
+ result, err = Sort(this.sequence, this.sortByField, this.sortAsc)
+ }
+ if err != nil {
+ t.Errorf("[%d] failed: %s", i, err)
+ continue
+ }
+ if !reflect.DeepEqual(result, this.expect) {
+ t.Errorf("[%d] Sort called on sequence: %v | sortByField: `%v` | got %v but expected %v", i, this.sequence, this.sortByField, result, this.expect)
+ }
+ }
+}
+
+func TestReturnWhenSet(t *testing.T) {
+ for i, this := range []struct {
+ data interface{}
+ key interface{}
+ expect interface{}
+ }{
+ {[]int{1, 2, 3}, 1, int64(2)},
+ {[]uint{1, 2, 3}, 1, uint64(2)},
+ {[]float64{1.1, 2.2, 3.3}, 1, float64(2.2)},
+ {[]string{"foo", "bar", "baz"}, 1, "bar"},
+ {[]TstX{{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"}}, 1, ""},
+ {map[string]int{"foo": 1, "bar": 2, "baz": 3}, "bar", int64(2)},
+ {map[string]uint{"foo": 1, "bar": 2, "baz": 3}, "bar", uint64(2)},
+ {map[string]float64{"foo": 1.1, "bar": 2.2, "baz": 3.3}, "bar", float64(2.2)},
+ {map[string]string{"foo": "FOO", "bar": "BAR", "baz": "BAZ"}, "bar", "BAR"},
+ {map[string]TstX{"foo": {A: "a", B: "b"}, "bar": {A: "c", B: "d"}, "baz": {A: "e", B: "f"}}, "bar", ""},
+ {(*[]string)(nil), "bar", ""},
+ } {
+ result := ReturnWhenSet(this.data, this.key)
+ if !reflect.DeepEqual(result, this.expect) {
+ t.Errorf("[%d] ReturnWhenSet got %v (type %v) but expected %v (type %v)", i, result, reflect.TypeOf(result), this.expect, reflect.TypeOf(this.expect))
+ }
+ }
+}
+
+func TestMarkdownify(t *testing.T) {
+
+ result := Markdownify("Hello **World!**")
+
+ expect := template.HTML("Hello <strong>World!</strong>")
+
+ if result != expect {
+ t.Errorf("Markdownify: got '%s', expected '%s'", result, expect)
+ }
+}
+
+func TestApply(t *testing.T) {
+ strings := []interface{}{"a\n", "b\n"}
+ noStringers := []interface{}{tstNoStringer{}, tstNoStringer{}}
+
+ var nilErr *error = nil
+
+ chomped, _ := Apply(strings, "chomp", ".")
+ assert.Equal(t, []interface{}{"a", "b"}, chomped)
+
+ chomped, _ = Apply(strings, "chomp", "c\n")
+ assert.Equal(t, []interface{}{"c", "c"}, chomped)
+
+ chomped, _ = Apply(nil, "chomp", ".")
+ assert.Equal(t, []interface{}{}, chomped)
+
+ _, err := Apply(strings, "apply", ".")
+ if err == nil {
+ t.Errorf("apply with apply should fail")
+ }
+
+ _, err = Apply(nilErr, "chomp", ".")
+ if err == nil {
+ t.Errorf("apply with nil in seq should fail")
+ }
+
+ _, err = Apply(strings, "dobedobedo", ".")
+ if err == nil {
+ t.Errorf("apply with unknown func should fail")
+ }
+
+ _, err = Apply(noStringers, "chomp", ".")
+ if err == nil {
+ t.Errorf("apply when func fails should fail")
+ }
+
+ _, err = Apply(tstNoStringer{}, "chomp", ".")
+ if err == nil {
+ t.Errorf("apply with non-sequence should fail")
+ }
+
+}
+
+func TestChomp(t *testing.T) {
+ base := "\n This is\na story "
+ for i, item := range []string{
+ "\n", "\n\n",
+ "\r", "\r\r",
+ "\r\n", "\r\n\r\n",
+ } {
+ chomped, _ := Chomp(base + item)
+
+ if chomped != base {
+ t.Errorf("[%d] Chomp failed, got '%v'", i, chomped)
+ }
+
+ _, err := Chomp(tstNoStringer{})
+
+ if err == nil {
+ t.Errorf("Chomp should fail")
+ }
+ }
+}
+
+func TestReplace(t *testing.T) {
+ v, _ := Replace("aab", "a", "b")
+ assert.Equal(t, "bbb", v)
+ v, _ = Replace("11a11", 1, 2)
+ assert.Equal(t, "22a22", v)
+ v, _ = Replace(12345, 1, 2)
+ assert.Equal(t, "22345", v)
+ _, e := Replace(tstNoStringer{}, "a", "b")
+ assert.NotNil(t, e, "tstNoStringer isn't trimmable")
+ _, e = Replace("a", tstNoStringer{}, "b")
+ assert.NotNil(t, e, "tstNoStringer cannot be converted to string")
+ _, e = Replace("a", "b", tstNoStringer{})
+ assert.NotNil(t, e, "tstNoStringer cannot be converted to string")
+}
+
+func TestTrim(t *testing.T) {
+ v, _ := Trim("1234 my way 13", "123")
+ assert.Equal(t, "4 my way ", v)
+ v, _ = Trim(" my way ", " ")
+ assert.Equal(t, "my way", v)
+ v, _ = Trim(1234, "14")
+ assert.Equal(t, "23", v)
+ _, e := Trim(tstNoStringer{}, " ")
+ assert.NotNil(t, e, "tstNoStringer isn't trimmable")
+}
+
+func TestDateFormat(t *testing.T) {
+ for i, this := range []struct {
+ layout string
+ value interface{}
+ expect interface{}
+ }{
+ {"Monday, Jan 2, 2006", "2015-01-21", "Wednesday, Jan 21, 2015"},
+ {"Monday, Jan 2, 2006", time.Date(2015, time.January, 21, 0, 0, 0, 0, time.UTC), "Wednesday, Jan 21, 2015"},
+ {"This isn't a date layout string", "2015-01-21", "This isn't a date layout string"},
+ {"Monday, Jan 2, 2006", 1421733600, false},
+ {"Monday, Jan 2, 2006", 1421733600.123, false},
+ } {
+ result, err := DateFormat(this.layout, this.value)
+ if b, ok := this.expect.(bool); ok && !b {
+ if err == nil {
+ t.Errorf("[%d] DateFormat didn't return an expected error", i)
+ }
+ } else {
+ if err != nil {
+ t.Errorf("[%d] DateFormat failed: %s", i, err)
+ continue
+ }
+ if result != this.expect {
+ t.Errorf("[%d] DateFormat got %v but expected %v", i, result, this.expect)
+ }
+ }
+ }
+}
+
+func TestSafeHTML(t *testing.T) {
+ for i, this := range []struct {
+ str string
+ tmplStr string
+ expectWithoutEscape string
+ expectWithEscape string
+ }{
+ {`<div></div>`, `{{ . }}`, `<div></div>`, `<div></div>`},
+ } {
+ tmpl, err := template.New("test").Parse(this.tmplStr)
+ if err != nil {
+ t.Errorf("[%d] unable to create new html template %q: %s", i, this.tmplStr, err)
+ continue
+ }
+
+ buf := new(bytes.Buffer)
+ err = tmpl.Execute(buf, this.str)
+ if err != nil {
+ t.Errorf("[%d] execute template with a raw string value returns unexpected error: %s", i, err)
+ }
+ if buf.String() != this.expectWithoutEscape {
+ t.Errorf("[%d] execute template with a raw string value, got %v but expected %v", i, buf.String(), this.expectWithoutEscape)
+ }
+
+ buf.Reset()
+ err = tmpl.Execute(buf, SafeHTML(this.str))
+ if err != nil {
+ t.Errorf("[%d] execute template with an escaped string value by SafeHTML returns unexpected error: %s", i, err)
+ }
+ if buf.String() != this.expectWithEscape {
+ t.Errorf("[%d] execute template with an escaped string value by SafeHTML, got %v but expected %v", i, buf.String(), this.expectWithEscape)
+ }
+ }
+}
+
+func TestSafeHTMLAttr(t *testing.T) {
+ for i, this := range []struct {
+ str string
+ tmplStr string
+ expectWithoutEscape string
+ expectWithEscape string
+ }{
+ {`href="irc://irc.freenode.net/#golang"`, `<a {{ . }}>irc</a>`, `<a ZgotmplZ>irc</a>`, `<a href="irc://irc.freenode.net/#golang">irc</a>`},
+ } {
+ tmpl, err := template.New("test").Parse(this.tmplStr)
+ if err != nil {
+ t.Errorf("[%d] unable to create new html template %q: %s", i, this.tmplStr, err)
+ continue
+ }
+
+ buf := new(bytes.Buffer)
+ err = tmpl.Execute(buf, this.str)
+ if err != nil {
+ t.Errorf("[%d] execute template with a raw string value returns unexpected error: %s", i, err)
+ }
+ if buf.String() != this.expectWithoutEscape {
+ t.Errorf("[%d] execute template with a raw string value, got %v but expected %v", i, buf.String(), this.expectWithoutEscape)
+ }
+
+ buf.Reset()
+ err = tmpl.Execute(buf, SafeHTMLAttr(this.str))
+ if err != nil {
+ t.Errorf("[%d] execute template with an escaped string value by SafeHTMLAttr returns unexpected error: %s", i, err)
+ }
+ if buf.String() != this.expectWithEscape {
+ t.Errorf("[%d] execute template with an escaped string value by SafeHTMLAttr, got %v but expected %v", i, buf.String(), this.expectWithEscape)
+ }
+ }
+}
+
+func TestSafeCSS(t *testing.T) {
+ for i, this := range []struct {
+ str string
+ tmplStr string
+ expectWithoutEscape string
+ expectWithEscape string
+ }{
+ {`width: 60px;`, `<div style="{{ . }}"></div>`, `<div style="ZgotmplZ"></div>`, `<div style="width: 60px;"></div>`},
+ } {
+ tmpl, err := template.New("test").Parse(this.tmplStr)
+ if err != nil {
+ t.Errorf("[%d] unable to create new html template %q: %s", i, this.tmplStr, err)
+ continue
+ }
+
+ buf := new(bytes.Buffer)
+ err = tmpl.Execute(buf, this.str)
+ if err != nil {
+ t.Errorf("[%d] execute template with a raw string value returns unexpected error: %s", i, err)
+ }
+ if buf.String() != this.expectWithoutEscape {
+ t.Errorf("[%d] execute template with a raw string value, got %v but expected %v", i, buf.String(), this.expectWithoutEscape)
+ }
+
+ buf.Reset()
+ err = tmpl.Execute(buf, SafeCSS(this.str))
+ if err != nil {
+ t.Errorf("[%d] execute template with an escaped string value by SafeCSS returns unexpected error: %s", i, err)
+ }
+ if buf.String() != this.expectWithEscape {
+ t.Errorf("[%d] execute template with an escaped string value by SafeCSS, got %v but expected %v", i, buf.String(), this.expectWithEscape)
+ }
+ }
+}
+
+func TestSafeURL(t *testing.T) {
+ for i, this := range []struct {
+ str string
+ tmplStr string
+ expectWithoutEscape string
+ expectWithEscape string
+ }{
+ {`irc://irc.freenode.net/#golang`, `<a href="{{ . }}">IRC</a>`, `<a href="#ZgotmplZ">IRC</a>`, `<a href="irc://irc.freenode.net/#golang">IRC</a>`},
+ } {
+ tmpl, err := template.New("test").Parse(this.tmplStr)
+ if err != nil {
+ t.Errorf("[%d] unable to create new html template %q: %s", i, this.tmplStr, err)
+ continue
+ }
+
+ buf := new(bytes.Buffer)
+ err = tmpl.Execute(buf, this.str)
+ if err != nil {
+ t.Errorf("[%d] execute template with a raw string value returns unexpected error: %s", i, err)
+ }
+ if buf.String() != this.expectWithoutEscape {
+ t.Errorf("[%d] execute template with a raw string value, got %v but expected %v", i, buf.String(), this.expectWithoutEscape)
+ }
+
+ buf.Reset()
+ err = tmpl.Execute(buf, SafeURL(this.str))
+ if err != nil {
+ t.Errorf("[%d] execute template with an escaped string value by SafeURL returns unexpected error: %s", i, err)
+ }
+ if buf.String() != this.expectWithEscape {
+ t.Errorf("[%d] execute template with an escaped string value by SafeURL, got %v but expected %v", i, buf.String(), this.expectWithEscape)
+ }
+ }
+}
--- a/tpl/template_test.go
+++ b/tpl/template_test.go
@@ -1,1222 +1,3 @@
package tpl
-import (
- "bytes"
- "errors"
- "fmt"
- "github.com/stretchr/testify/assert"
- "html/template"
- "path"
- "reflect"
- "runtime"
- "testing"
- "time"
-)
-
-type tstNoStringer struct {
-}
-
-type tstCompareType int
-
-const (
- tstEq tstCompareType = iota
- tstNe
- tstGt
- tstGe
- tstLt
- tstLe
-)
-
-func tstIsEq(tp tstCompareType) bool {
- return tp == tstEq || tp == tstGe || tp == tstLe
-}
-
-func tstIsGt(tp tstCompareType) bool {
- return tp == tstGt || tp == tstGe
-}
-
-func tstIsLt(tp tstCompareType) bool {
- return tp == tstLt || tp == tstLe
-}
-
-func TestCompare(t *testing.T) {
- for _, this := range []struct {
- tstCompareType
- funcUnderTest func(a, b interface{}) bool
- }{
- {tstGt, Gt},
- {tstLt, Lt},
- {tstGe, Ge},
- {tstLe, Le},
- {tstEq, Eq},
- {tstNe, Ne},
- } {
- doTestCompare(t, this.tstCompareType, this.funcUnderTest)
- }
-
-}
-
-func doTestCompare(t *testing.T, tp tstCompareType, funcUnderTest func(a, b interface{}) bool) {
- for i, this := range []struct {
- left interface{}
- right interface{}
- expectIndicator int
- }{
- {5, 8, -1},
- {8, 5, 1},
- {5, 5, 0},
- {int(5), int64(5), 0},
- {int32(5), int(5), 0},
- {int16(4), int(5), -1},
- {uint(15), uint64(15), 0},
- {-2, 1, -1},
- {2, -5, 1},
- {0.0, 1.23, -1},
- {1.1, 1.1, 0},
- {float32(1.0), float64(1.0), 0},
- {1.23, 0.0, 1},
- {"5", "5", 0},
- {"8", "5", 1},
- {"5", "0001", 1},
- {[]int{100, 99}, []int{1, 2, 3, 4}, -1},
- } {
- result := funcUnderTest(this.left, this.right)
- success := false
-
- if this.expectIndicator == 0 {
- if tstIsEq(tp) {
- success = result
- } else {
- success = !result
- }
- }
-
- if this.expectIndicator < 0 {
- success = result && (tstIsLt(tp) || tp == tstNe)
- success = success || (!result && !tstIsLt(tp))
- }
-
- if this.expectIndicator > 0 {
- success = result && (tstIsGt(tp) || tp == tstNe)
- success = success || (!result && (!tstIsGt(tp) || tp != tstNe))
- }
-
- if !success {
- t.Errorf("[%d][%s] %v compared to %v: %t", i, path.Base(runtime.FuncForPC(reflect.ValueOf(funcUnderTest).Pointer()).Name()), this.left, this.right, result)
- }
- }
-}
-
-func TestArethmic(t *testing.T) {
- for i, this := range []struct {
- a interface{}
- b interface{}
- op rune
- expect interface{}
- }{
- {1, 2, '+', int64(3)},
- {1, 2, '-', int64(-1)},
- {2, 2, '*', int64(4)},
- {4, 2, '/', int64(2)},
- {uint8(1), uint8(3), '+', uint64(4)},
- {uint8(3), uint8(2), '-', uint64(1)},
- {uint8(2), uint8(2), '*', uint64(4)},
- {uint16(4), uint8(2), '/', uint64(2)},
- {4, 2, '¤', false},
- {4, 0, '/', false},
- } {
- // TODO(bep): Take precision into account.
- result, err := doArithmetic(this.a, this.b, this.op)
- if b, ok := this.expect.(bool); ok && !b {
- if err == nil {
- t.Errorf("[%d] doArethmic didn't return an expected error", i)
- }
- } else {
- if err != nil {
- t.Errorf("[%d] failed: %s", i, err)
- continue
- }
- if !reflect.DeepEqual(result, this.expect) {
- t.Errorf("[%d] doArethmic got %v (%T) but expected %v (%T)", i, result, result, this.expect, this.expect)
- }
- }
- }
-}
-
-func TestMod(t *testing.T) {
- for i, this := range []struct {
- a interface{}
- b interface{}
- expect interface{}
- }{
- {3, 2, int64(1)},
- {3, 1, int64(0)},
- {3, 0, false},
- {0, 3, int64(0)},
- {3.1, 2, false},
- {3, 2.1, false},
- {3.1, 2.1, false},
- {int8(3), int8(2), int64(1)},
- {int16(3), int16(2), int64(1)},
- {int32(3), int32(2), int64(1)},
- {int64(3), int64(2), int64(1)},
- } {
- result, err := Mod(this.a, this.b)
- if b, ok := this.expect.(bool); ok && !b {
- if err == nil {
- t.Errorf("[%d] modulo didn't return an expected error", i)
- }
- } else {
- if err != nil {
- t.Errorf("[%d] failed: %s", i, err)
- continue
- }
- if !reflect.DeepEqual(result, this.expect) {
- t.Errorf("[%d] modulo got %v but expected %v", i, result, this.expect)
- }
- }
- }
-}
-
-func TestModBool(t *testing.T) {
- for i, this := range []struct {
- a interface{}
- b interface{}
- expect interface{}
- }{
- {3, 3, true},
- {3, 2, false},
- {3, 1, true},
- {3, 0, nil},
- {0, 3, true},
- {3.1, 2, nil},
- {3, 2.1, nil},
- {3.1, 2.1, nil},
- {int8(3), int8(3), true},
- {int8(3), int8(2), false},
- {int16(3), int16(3), true},
- {int16(3), int16(2), false},
- {int32(3), int32(3), true},
- {int32(3), int32(2), false},
- {int64(3), int64(3), true},
- {int64(3), int64(2), false},
- } {
- result, err := ModBool(this.a, this.b)
- if this.expect == nil {
- if err == nil {
- t.Errorf("[%d] modulo didn't return an expected error", i)
- }
- } else {
- if err != nil {
- t.Errorf("[%d] failed: %s", i, err)
- continue
- }
- if !reflect.DeepEqual(result, this.expect) {
- t.Errorf("[%d] modulo got %v but expected %v", i, result, this.expect)
- }
- }
- }
-}
-
-func TestFirst(t *testing.T) {
- for i, this := range []struct {
- count interface{}
- sequence interface{}
- expect interface{}
- }{
- {int(2), []string{"a", "b", "c"}, []string{"a", "b"}},
- {int32(3), []string{"a", "b"}, []string{"a", "b"}},
- {int64(2), []int{100, 200, 300}, []int{100, 200}},
- {100, []int{100, 200}, []int{100, 200}},
- {"1", []int{100, 200, 300}, []int{100}},
- {int64(-1), []int{100, 200, 300}, false},
- {"noint", []int{100, 200, 300}, false},
- {1, nil, false},
- {nil, []int{100}, false},
- {1, t, false},
- } {
- results, err := First(this.count, this.sequence)
- if b, ok := this.expect.(bool); ok && !b {
- if err == nil {
- t.Errorf("[%d] First didn't return an expected error", i)
- }
- } else {
- if err != nil {
- t.Errorf("[%d] failed: %s", i, err)
- continue
- }
- if !reflect.DeepEqual(results, this.expect) {
- t.Errorf("[%d] First %d items, got %v but expected %v", i, this.count, results, this.expect)
- }
- }
- }
-}
-
-func TestIn(t *testing.T) {
- for i, this := range []struct {
- v1 interface{}
- v2 interface{}
- expect bool
- }{
- {[]string{"a", "b", "c"}, "b", true},
- {[]string{"a", "b", "c"}, "d", false},
- {[]string{"a", "12", "c"}, 12, false},
- {[]int{1, 2, 4}, 2, true},
- {[]int{1, 2, 4}, 3, false},
- {[]float64{1.23, 2.45, 4.67}, 1.23, true},
- {[]float64{1.234567, 2.45, 4.67}, 1.234568, false},
- {"this substring should be found", "substring", true},
- {"this substring should not be found", "subseastring", false},
- } {
- result := In(this.v1, this.v2)
-
- if result != this.expect {
- t.Errorf("[%d] Got %v but expected %v", i, result, this.expect)
- }
- }
-}
-
-func TestSlicestr(t *testing.T) {
- for i, this := range []struct {
- v1 interface{}
- v2 []int
- expect interface{}
- }{
- {"abc", []int{1, 2}, "b"},
- {"abc", []int{1, 3}, "bc"},
- {"abc", []int{0, 1}, "a"},
- {"abcdef", []int{}, "abcdef"},
- {"abcdef", []int{0, 6}, "abcdef"},
- {"abcdef", []int{0, 2}, "ab"},
- {"abcdef", []int{2}, "cdef"},
- {123, []int{1, 3}, "23"},
- {123, []int{1, 2, 3}, false},
- {tstNoStringer{}, []int{0, 1}, false},
- } {
- result, err := Slicestr(this.v1, this.v2...)
-
- if b, ok := this.expect.(bool); ok && !b {
- if err == nil {
- t.Errorf("[%d] Slice didn't return an expected error", i)
- }
- } else {
- if err != nil {
- t.Errorf("[%d] failed: %s", i, err)
- continue
- }
- if !reflect.DeepEqual(result, this.expect) {
- t.Errorf("[%d] Got %s but expected %s", i, result, this.expect)
- }
- }
- }
-}
-
-func TestSubstr(t *testing.T) {
- for i, this := range []struct {
- v1 interface{}
- v2 int
- v3 int
- expect interface{}
- }{
- {"abc", 1, 2, "bc"},
- {"abc", 0, 1, "a"},
- {"abcdef", -1, 2, "ef"},
- {"abcdef", -3, 3, "bcd"},
- {"abcdef", 0, -1, "abcde"},
- {"abcdef", 2, -1, "cde"},
- {"abcdef", 4, -4, false},
- {"abcdef", 7, 1, false},
- {"abcdef", 1, 100, "bcdef"},
- {"abcdef", -100, 3, "abc"},
- {"abcdef", -3, -1, "de"},
- {123, 1, 3, "23"},
- {1.2e3, 0, 4, "1200"},
- {tstNoStringer{}, 0, 1, false},
- } {
- result, err := Substr(this.v1, this.v2, this.v3)
-
- if b, ok := this.expect.(bool); ok && !b {
- if err == nil {
- t.Errorf("[%d] Substr didn't return an expected error", i)
- }
- } else {
- if err != nil {
- t.Errorf("[%d] failed: %s", i, err)
- continue
- }
- if !reflect.DeepEqual(result, this.expect) {
- t.Errorf("[%d] Got %s but expected %s", i, result, this.expect)
- }
- }
- }
-}
-
-func TestSplit(t *testing.T) {
- for i, this := range []struct {
- v1 interface{}
- v2 string
- expect interface{}
- }{
- {"a, b", ", ", []string{"a", "b"}},
- {"a & b & c", " & ", []string{"a", "b", "c"}},
- {"http://exmaple.com", "http://", []string{"", "exmaple.com"}},
- {123, "2", []string{"1", "3"}},
- {tstNoStringer{}, ",", false},
- } {
- result, err := Split(this.v1, this.v2)
-
- if b, ok := this.expect.(bool); ok && !b {
- if err == nil {
- t.Errorf("[%d] Split didn't return an expected error", i)
- }
- } else {
- if err != nil {
- t.Errorf("[%d] failed: %s", i, err)
- continue
- }
- if !reflect.DeepEqual(result, this.expect) {
- t.Errorf("[%d] Got %s but expected %s", i, result, this.expect)
- }
- }
- }
-
-}
-
-func TestIntersect(t *testing.T) {
- for i, this := range []struct {
- sequence1 interface{}
- sequence2 interface{}
- expect interface{}
- }{
- {[]string{"a", "b", "c"}, []string{"a", "b"}, []string{"a", "b"}},
- {[]string{"a", "b"}, []string{"a", "b", "c"}, []string{"a", "b"}},
- {[]string{"a", "b", "c"}, []string{"d", "e"}, []string{}},
- {[]string{}, []string{}, []string{}},
- {[]string{"a", "b"}, nil, make([]interface{}, 0)},
- {nil, []string{"a", "b"}, make([]interface{}, 0)},
- {nil, nil, make([]interface{}, 0)},
- {[]string{"1", "2"}, []int{1, 2}, []string{}},
- {[]int{1, 2}, []string{"1", "2"}, []int{}},
- {[]int{1, 2, 4}, []int{2, 4}, []int{2, 4}},
- {[]int{2, 4}, []int{1, 2, 4}, []int{2, 4}},
- {[]int{1, 2, 4}, []int{3, 6}, []int{}},
- {[]float64{2.2, 4.4}, []float64{1.1, 2.2, 4.4}, []float64{2.2, 4.4}},
- } {
- results, err := Intersect(this.sequence1, this.sequence2)
- if err != nil {
- t.Errorf("[%d] failed: %s", i, err)
- continue
- }
- if !reflect.DeepEqual(results, this.expect) {
- t.Errorf("[%d] Got %v but expected %v", i, results, this.expect)
- }
- }
-
- _, err1 := Intersect("not an array or slice", []string{"a"})
-
- if err1 == nil {
- t.Error("Excpected error for non array as first arg")
- }
-
- _, err2 := Intersect([]string{"a"}, "not an array or slice")
-
- if err2 == nil {
- t.Error("Excpected error for non array as second arg")
- }
-}
-
-func TestIsSet(t *testing.T) {
- aSlice := []interface{}{1, 2, 3, 5}
- aMap := map[string]interface{}{"a": 1, "b": 2}
-
- assert.True(t, IsSet(aSlice, 2))
- assert.True(t, IsSet(aMap, "b"))
- assert.False(t, IsSet(aSlice, 22))
- assert.False(t, IsSet(aMap, "bc"))
-}
-
-func (x *TstX) TstRp() string {
- return "r" + x.A
-}
-
-func (x TstX) TstRv() string {
- return "r" + x.B
-}
-
-func (x TstX) unexportedMethod() string {
- return x.unexported
-}
-
-func (x TstX) MethodWithArg(s string) string {
- return s
-}
-
-func (x TstX) MethodReturnNothing() {}
-
-func (x TstX) MethodReturnErrorOnly() error {
- return errors.New("something error occured")
-}
-
-func (x TstX) MethodReturnTwoValues() (string, string) {
- return "foo", "bar"
-}
-
-func (x TstX) MethodReturnValueWithError() (string, error) {
- return "", errors.New("something error occured")
-}
-
-func (x TstX) String() string {
- return fmt.Sprintf("A: %s, B: %s", x.A, x.B)
-}
-
-type TstX struct {
- A, B string
- unexported string
-}
-
-func TestEvaluateSubElem(t *testing.T) {
- tstx := TstX{A: "foo", B: "bar"}
- var inner struct {
- S fmt.Stringer
- }
- inner.S = tstx
- interfaceValue := reflect.ValueOf(&inner).Elem().Field(0)
-
- for i, this := range []struct {
- value reflect.Value
- key string
- expect interface{}
- }{
- {reflect.ValueOf(tstx), "A", "foo"},
- {reflect.ValueOf(&tstx), "TstRp", "rfoo"},
- {reflect.ValueOf(tstx), "TstRv", "rbar"},
- //{reflect.ValueOf(map[int]string{1: "foo", 2: "bar"}), 1, "foo"},
- {reflect.ValueOf(map[string]string{"key1": "foo", "key2": "bar"}), "key1", "foo"},
- {interfaceValue, "String", "A: foo, B: bar"},
- {reflect.Value{}, "foo", false},
- //{reflect.ValueOf(map[int]string{1: "foo", 2: "bar"}), 1.2, false},
- {reflect.ValueOf(tstx), "unexported", false},
- {reflect.ValueOf(tstx), "unexportedMethod", false},
- {reflect.ValueOf(tstx), "MethodWithArg", false},
- {reflect.ValueOf(tstx), "MethodReturnNothing", false},
- {reflect.ValueOf(tstx), "MethodReturnErrorOnly", false},
- {reflect.ValueOf(tstx), "MethodReturnTwoValues", false},
- {reflect.ValueOf(tstx), "MethodReturnValueWithError", false},
- {reflect.ValueOf((*TstX)(nil)), "A", false},
- {reflect.ValueOf(tstx), "C", false},
- {reflect.ValueOf(map[int]string{1: "foo", 2: "bar"}), "1", false},
- {reflect.ValueOf([]string{"foo", "bar"}), "1", false},
- } {
- result, err := evaluateSubElem(this.value, this.key)
- if b, ok := this.expect.(bool); ok && !b {
- if err == nil {
- t.Errorf("[%d] evaluateSubElem didn't return an expected error", i)
- }
- } else {
- if err != nil {
- t.Errorf("[%d] failed: %s", i, err)
- continue
- }
- if result.Kind() != reflect.String || result.String() != this.expect {
- t.Errorf("[%d] evaluateSubElem with %v got %v but expected %v", i, this.key, result, this.expect)
- }
- }
- }
-}
-
-func TestCheckCondition(t *testing.T) {
- type expect struct {
- result bool
- isError bool
- }
-
- for i, this := range []struct {
- value reflect.Value
- match reflect.Value
- op string
- expect
- }{
- {reflect.ValueOf(123), reflect.ValueOf(123), "", expect{true, false}},
- {reflect.ValueOf("foo"), reflect.ValueOf("foo"), "", expect{true, false}},
- {reflect.ValueOf(123), reflect.ValueOf(456), "!=", expect{true, false}},
- {reflect.ValueOf("foo"), reflect.ValueOf("bar"), "!=", expect{true, false}},
- {reflect.ValueOf(456), reflect.ValueOf(123), ">=", expect{true, false}},
- {reflect.ValueOf("foo"), reflect.ValueOf("bar"), ">=", expect{true, false}},
- {reflect.ValueOf(456), reflect.ValueOf(123), ">", expect{true, false}},
- {reflect.ValueOf("foo"), reflect.ValueOf("bar"), ">", expect{true, false}},
- {reflect.ValueOf(123), reflect.ValueOf(456), "<=", expect{true, false}},
- {reflect.ValueOf("bar"), reflect.ValueOf("foo"), "<=", expect{true, false}},
- {reflect.ValueOf(123), reflect.ValueOf(456), "<", expect{true, false}},
- {reflect.ValueOf("bar"), reflect.ValueOf("foo"), "<", expect{true, false}},
- {reflect.ValueOf(123), reflect.ValueOf([]int{123, 45, 678}), "in", expect{true, false}},
- {reflect.ValueOf("foo"), reflect.ValueOf([]string{"foo", "bar", "baz"}), "in", expect{true, false}},
- {reflect.ValueOf(123), reflect.ValueOf([]int{45, 678}), "not in", expect{true, false}},
- {reflect.ValueOf("foo"), reflect.ValueOf([]string{"bar", "baz"}), "not in", expect{true, false}},
- {reflect.ValueOf("foo"), reflect.ValueOf("bar-foo-baz"), "in", expect{true, false}},
- {reflect.ValueOf("foo"), reflect.ValueOf("bar--baz"), "not in", expect{true, false}},
- {reflect.Value{}, reflect.ValueOf("foo"), "", expect{false, false}},
- {reflect.ValueOf("foo"), reflect.Value{}, "", expect{false, false}},
- {reflect.ValueOf((*TstX)(nil)), reflect.ValueOf("foo"), "", expect{false, false}},
- {reflect.ValueOf("foo"), reflect.ValueOf((*TstX)(nil)), "", expect{false, false}},
- {reflect.ValueOf("foo"), reflect.ValueOf(map[int]string{}), "", expect{false, false}},
- {reflect.ValueOf("foo"), reflect.ValueOf([]int{1, 2}), "", expect{false, false}},
- {reflect.ValueOf(123), reflect.ValueOf([]int{}), "in", expect{false, false}},
- {reflect.ValueOf(123), reflect.ValueOf(123), "op", expect{false, true}},
- } {
- result, err := checkCondition(this.value, this.match, this.op)
- if this.expect.isError {
- if err == nil {
- t.Errorf("[%d] checkCondition didn't return an expected error", i)
- }
- } else {
- if err != nil {
- t.Errorf("[%d] failed: %s", i, err)
- continue
- }
- if result != this.expect.result {
- t.Errorf("[%d] check condition %v %s %v, got %v but expected %v", i, this.value, this.op, this.match, result, this.expect.result)
- }
- }
- }
-}
-
-func TestWhere(t *testing.T) {
- // TODO(spf): Put these page tests back in
- //page1 := &Page{contentType: "v", Source: Source{File: *source.NewFile("/x/y/z/source.md")}}
- //page2 := &Page{contentType: "w", Source: Source{File: *source.NewFile("/y/z/a/source.md")}}
-
- type Mid struct {
- Tst TstX
- }
-
- for i, this := range []struct {
- sequence interface{}
- key interface{}
- op string
- match interface{}
- expect interface{}
- }{
- {
- sequence: []map[int]string{
- {1: "a", 2: "m"}, {1: "c", 2: "d"}, {1: "e", 3: "m"},
- },
- key: 2, match: "m",
- expect: []map[int]string{
- {1: "a", 2: "m"},
- },
- },
- {
- sequence: []map[string]int{
- {"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "x": 4},
- },
- key: "b", match: 4,
- expect: []map[string]int{
- {"a": 3, "b": 4},
- },
- },
- {
- sequence: []TstX{
- {A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"},
- },
- key: "B", match: "f",
- expect: []TstX{
- {A: "e", B: "f"},
- },
- },
- {
- sequence: []*map[int]string{
- {1: "a", 2: "m"}, {1: "c", 2: "d"}, {1: "e", 3: "m"},
- },
- key: 2, match: "m",
- expect: []*map[int]string{
- {1: "a", 2: "m"},
- },
- },
- {
- sequence: []*TstX{
- {A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"},
- },
- key: "B", match: "f",
- expect: []*TstX{
- {A: "e", B: "f"},
- },
- },
- {
- sequence: []*TstX{
- {A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "c"},
- },
- key: "TstRp", match: "rc",
- expect: []*TstX{
- {A: "c", B: "d"},
- },
- },
- {
- sequence: []TstX{
- {A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "c"},
- },
- key: "TstRv", match: "rc",
- expect: []TstX{
- {A: "e", B: "c"},
- },
- },
- {
- sequence: []map[string]TstX{
- {"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}},
- },
- key: "foo.B", match: "d",
- expect: []map[string]TstX{
- {"foo": TstX{A: "c", B: "d"}},
- },
- },
- {
- sequence: []map[string]TstX{
- {"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}},
- },
- key: ".foo.B", match: "d",
- expect: []map[string]TstX{
- {"foo": TstX{A: "c", B: "d"}},
- },
- },
- {
- sequence: []map[string]TstX{
- {"foo": TstX{A: "a", B: "b"}}, {"foo": TstX{A: "c", B: "d"}}, {"foo": TstX{A: "e", B: "f"}},
- },
- key: "foo.TstRv", match: "rd",
- expect: []map[string]TstX{
- {"foo": TstX{A: "c", B: "d"}},
- },
- },
- {
- sequence: []map[string]*TstX{
- {"foo": &TstX{A: "a", B: "b"}}, {"foo": &TstX{A: "c", B: "d"}}, {"foo": &TstX{A: "e", B: "f"}},
- },
- key: "foo.TstRp", match: "rc",
- expect: []map[string]*TstX{
- {"foo": &TstX{A: "c", B: "d"}},
- },
- },
- {
- sequence: []map[string]Mid{
- {"foo": Mid{Tst: TstX{A: "a", B: "b"}}}, {"foo": Mid{Tst: TstX{A: "c", B: "d"}}}, {"foo": Mid{Tst: TstX{A: "e", B: "f"}}},
- },
- key: "foo.Tst.B", match: "d",
- expect: []map[string]Mid{
- {"foo": Mid{Tst: TstX{A: "c", B: "d"}}},
- },
- },
- {
- sequence: []map[string]Mid{
- {"foo": Mid{Tst: TstX{A: "a", B: "b"}}}, {"foo": Mid{Tst: TstX{A: "c", B: "d"}}}, {"foo": Mid{Tst: TstX{A: "e", B: "f"}}},
- },
- key: "foo.Tst.TstRv", match: "rd",
- expect: []map[string]Mid{
- {"foo": Mid{Tst: TstX{A: "c", B: "d"}}},
- },
- },
- {
- sequence: []map[string]*Mid{
- {"foo": &Mid{Tst: TstX{A: "a", B: "b"}}}, {"foo": &Mid{Tst: TstX{A: "c", B: "d"}}}, {"foo": &Mid{Tst: TstX{A: "e", B: "f"}}},
- },
- key: "foo.Tst.TstRp", match: "rc",
- expect: []map[string]*Mid{
- {"foo": &Mid{Tst: TstX{A: "c", B: "d"}}},
- },
- },
- {
- sequence: []map[string]int{
- {"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "b": 6},
- },
- key: "b", op: ">", match: 3,
- expect: []map[string]int{
- {"a": 3, "b": 4}, {"a": 5, "b": 6},
- },
- },
- {
- sequence: []TstX{
- {A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"},
- },
- key: "B", op: "!=", match: "f",
- expect: []TstX{
- {A: "a", B: "b"}, {A: "c", B: "d"},
- },
- },
- {
- sequence: []map[string]int{
- {"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "b": 6},
- },
- key: "b", op: "in", match: []int{3, 4, 5},
- expect: []map[string]int{
- {"a": 3, "b": 4},
- },
- },
- {
- sequence: []TstX{
- {A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"},
- },
- key: "B", op: "not in", match: []string{"c", "d", "e"},
- expect: []TstX{
- {A: "a", B: "b"}, {A: "e", B: "f"},
- },
- },
- {sequence: (*[]TstX)(nil), key: "A", match: "a", expect: false},
- {sequence: TstX{A: "a", B: "b"}, key: "A", match: "a", expect: false},
- {sequence: []map[string]*TstX{{"foo": nil}}, key: "foo.B", match: "d", expect: false},
- {
- sequence: []TstX{
- {A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"},
- },
- key: "B", op: "op", match: "f",
- expect: false,
- },
- //{[]*Page{page1, page2}, "Type", "v", []*Page{page1}},
- //{[]*Page{page1, page2}, "Section", "y", []*Page{page2}},
- } {
- var results interface{}
- var err error
- if len(this.op) > 0 {
- results, err = Where(this.sequence, this.key, this.op, this.match)
- } else {
- results, err = Where(this.sequence, this.key, this.match)
- }
- if b, ok := this.expect.(bool); ok && !b {
- if err == nil {
- t.Errorf("[%d] Where didn't return an expected error", i)
- }
- } else {
- if err != nil {
- t.Errorf("[%d] failed: %s", i, err)
- continue
- }
- if !reflect.DeepEqual(results, this.expect) {
- t.Errorf("[%d] Where clause matching %v with %v, got %v but expected %v", i, this.key, this.match, results, this.expect)
- }
- }
- }
-
- var err error
- _, err = Where(map[string]int{"a": 1, "b": 2}, "a", []byte("="), 1)
- if err == nil {
- t.Errorf("Where called with none string op value didn't return an expected error")
- }
-
- _, err = Where(map[string]int{"a": 1, "b": 2}, "a", []byte("="), 1, 2)
- if err == nil {
- t.Errorf("Where called with more than two variable arguments didn't return an expected error")
- }
-
- _, err = Where(map[string]int{"a": 1, "b": 2}, "a")
- if err == nil {
- t.Errorf("Where called with no variable arguments didn't return an expected error")
- }
-}
-
-func TestDelimit(t *testing.T) {
- for i, this := range []struct {
- sequence interface{}
- delimiter interface{}
- last interface{}
- expect template.HTML
- }{
- {[]string{"class1", "class2", "class3"}, " ", nil, "class1 class2 class3"},
- {[]int{1, 2, 3, 4, 5}, ",", nil, "1,2,3,4,5"},
- {[]int{1, 2, 3, 4, 5}, ", ", nil, "1, 2, 3, 4, 5"},
- {[]string{"class1", "class2", "class3"}, " ", " and ", "class1 class2 and class3"},
- {[]int{1, 2, 3, 4, 5}, ",", ",", "1,2,3,4,5"},
- {[]int{1, 2, 3, 4, 5}, ", ", ", and ", "1, 2, 3, 4, and 5"},
- // test maps with and without sorting required
- {map[string]int{"1": 10, "2": 20, "3": 30, "4": 40, "5": 50}, "--", nil, "10--20--30--40--50"},
- {map[string]int{"3": 10, "2": 20, "1": 30, "4": 40, "5": 50}, "--", nil, "30--20--10--40--50"},
- {map[string]string{"1": "10", "2": "20", "3": "30", "4": "40", "5": "50"}, "--", nil, "10--20--30--40--50"},
- {map[string]string{"3": "10", "2": "20", "1": "30", "4": "40", "5": "50"}, "--", nil, "30--20--10--40--50"},
- {map[string]string{"one": "10", "two": "20", "three": "30", "four": "40", "five": "50"}, "--", nil, "50--40--10--30--20"},
- {map[int]string{1: "10", 2: "20", 3: "30", 4: "40", 5: "50"}, "--", nil, "10--20--30--40--50"},
- {map[int]string{3: "10", 2: "20", 1: "30", 4: "40", 5: "50"}, "--", nil, "30--20--10--40--50"},
- {map[float64]string{3.3: "10", 2.3: "20", 1.3: "30", 4.3: "40", 5.3: "50"}, "--", nil, "30--20--10--40--50"},
- // test maps with a last delimiter
- {map[string]int{"1": 10, "2": 20, "3": 30, "4": 40, "5": 50}, "--", "--and--", "10--20--30--40--and--50"},
- {map[string]int{"3": 10, "2": 20, "1": 30, "4": 40, "5": 50}, "--", "--and--", "30--20--10--40--and--50"},
- {map[string]string{"1": "10", "2": "20", "3": "30", "4": "40", "5": "50"}, "--", "--and--", "10--20--30--40--and--50"},
- {map[string]string{"3": "10", "2": "20", "1": "30", "4": "40", "5": "50"}, "--", "--and--", "30--20--10--40--and--50"},
- {map[string]string{"one": "10", "two": "20", "three": "30", "four": "40", "five": "50"}, "--", "--and--", "50--40--10--30--and--20"},
- {map[int]string{1: "10", 2: "20", 3: "30", 4: "40", 5: "50"}, "--", "--and--", "10--20--30--40--and--50"},
- {map[int]string{3: "10", 2: "20", 1: "30", 4: "40", 5: "50"}, "--", "--and--", "30--20--10--40--and--50"},
- {map[float64]string{3.5: "10", 2.5: "20", 1.5: "30", 4.5: "40", 5.5: "50"}, "--", "--and--", "30--20--10--40--and--50"},
- } {
- var result template.HTML
- var err error
- if this.last == nil {
- result, err = Delimit(this.sequence, this.delimiter)
- } else {
- result, err = Delimit(this.sequence, this.delimiter, this.last)
- }
- if err != nil {
- t.Errorf("[%d] failed: %s", i, err)
- continue
- }
- if !reflect.DeepEqual(result, this.expect) {
- t.Errorf("[%d] Delimit called on sequence: %v | delimiter: `%v` | last: `%v`, got %v but expected %v", i, this.sequence, this.delimiter, this.last, result, this.expect)
- }
- }
-}
-
-func TestSort(t *testing.T) {
- type ts struct {
- MyInt int
- MyFloat float64
- MyString string
- }
- for i, this := range []struct {
- sequence interface{}
- sortByField interface{}
- sortAsc string
- expect []interface{}
- }{
- {[]string{"class1", "class2", "class3"}, nil, "asc", []interface{}{"class1", "class2", "class3"}},
- {[]string{"class3", "class1", "class2"}, nil, "asc", []interface{}{"class1", "class2", "class3"}},
- {[]int{1, 2, 3, 4, 5}, nil, "asc", []interface{}{1, 2, 3, 4, 5}},
- {[]int{5, 4, 3, 1, 2}, nil, "asc", []interface{}{1, 2, 3, 4, 5}},
- // test map sorting by keys
- {map[string]int{"1": 10, "2": 20, "3": 30, "4": 40, "5": 50}, nil, "asc", []interface{}{10, 20, 30, 40, 50}},
- {map[string]int{"3": 10, "2": 20, "1": 30, "4": 40, "5": 50}, nil, "asc", []interface{}{30, 20, 10, 40, 50}},
- {map[string]string{"1": "10", "2": "20", "3": "30", "4": "40", "5": "50"}, nil, "asc", []interface{}{"10", "20", "30", "40", "50"}},
- {map[string]string{"3": "10", "2": "20", "1": "30", "4": "40", "5": "50"}, nil, "asc", []interface{}{"30", "20", "10", "40", "50"}},
- {map[string]string{"one": "10", "two": "20", "three": "30", "four": "40", "five": "50"}, nil, "asc", []interface{}{"50", "40", "10", "30", "20"}},
- {map[int]string{1: "10", 2: "20", 3: "30", 4: "40", 5: "50"}, nil, "asc", []interface{}{"10", "20", "30", "40", "50"}},
- {map[int]string{3: "10", 2: "20", 1: "30", 4: "40", 5: "50"}, nil, "asc", []interface{}{"30", "20", "10", "40", "50"}},
- {map[float64]string{3.3: "10", 2.3: "20", 1.3: "30", 4.3: "40", 5.3: "50"}, nil, "asc", []interface{}{"30", "20", "10", "40", "50"}},
- // test map sorting by value
- {map[string]int{"1": 10, "2": 20, "3": 30, "4": 40, "5": 50}, "value", "asc", []interface{}{10, 20, 30, 40, 50}},
- {map[string]int{"3": 10, "2": 20, "1": 30, "4": 40, "5": 50}, "value", "asc", []interface{}{10, 20, 30, 40, 50}},
- // test map sorting by field value
- {
- map[string]ts{"1": {10, 10.5, "ten"}, "2": {20, 20.5, "twenty"}, "3": {30, 30.5, "thirty"}, "4": {40, 40.5, "forty"}, "5": {50, 50.5, "fifty"}},
- "MyInt",
- "asc",
- []interface{}{ts{10, 10.5, "ten"}, ts{20, 20.5, "twenty"}, ts{30, 30.5, "thirty"}, ts{40, 40.5, "forty"}, ts{50, 50.5, "fifty"}},
- },
- {
- map[string]ts{"1": {10, 10.5, "ten"}, "2": {20, 20.5, "twenty"}, "3": {30, 30.5, "thirty"}, "4": {40, 40.5, "forty"}, "5": {50, 50.5, "fifty"}},
- "MyFloat",
- "asc",
- []interface{}{ts{10, 10.5, "ten"}, ts{20, 20.5, "twenty"}, ts{30, 30.5, "thirty"}, ts{40, 40.5, "forty"}, ts{50, 50.5, "fifty"}},
- },
- {
- map[string]ts{"1": {10, 10.5, "ten"}, "2": {20, 20.5, "twenty"}, "3": {30, 30.5, "thirty"}, "4": {40, 40.5, "forty"}, "5": {50, 50.5, "fifty"}},
- "MyString",
- "asc",
- []interface{}{ts{50, 50.5, "fifty"}, ts{40, 40.5, "forty"}, ts{10, 10.5, "ten"}, ts{30, 30.5, "thirty"}, ts{20, 20.5, "twenty"}},
- },
- // Test sort desc
- {[]string{"class1", "class2", "class3"}, "value", "desc", []interface{}{"class3", "class2", "class1"}},
- {[]string{"class3", "class1", "class2"}, "value", "desc", []interface{}{"class3", "class2", "class1"}},
- } {
- var result []interface{}
- var err error
- if this.sortByField == nil {
- result, err = Sort(this.sequence)
- } else {
- result, err = Sort(this.sequence, this.sortByField, this.sortAsc)
- }
- if err != nil {
- t.Errorf("[%d] failed: %s", i, err)
- continue
- }
- if !reflect.DeepEqual(result, this.expect) {
- t.Errorf("[%d] Sort called on sequence: %v | sortByField: `%v` | got %v but expected %v", i, this.sequence, this.sortByField, result, this.expect)
- }
- }
-}
-
-func TestReturnWhenSet(t *testing.T) {
- for i, this := range []struct {
- data interface{}
- key interface{}
- expect interface{}
- }{
- {[]int{1, 2, 3}, 1, int64(2)},
- {[]uint{1, 2, 3}, 1, uint64(2)},
- {[]float64{1.1, 2.2, 3.3}, 1, float64(2.2)},
- {[]string{"foo", "bar", "baz"}, 1, "bar"},
- {[]TstX{{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"}}, 1, ""},
- {map[string]int{"foo": 1, "bar": 2, "baz": 3}, "bar", int64(2)},
- {map[string]uint{"foo": 1, "bar": 2, "baz": 3}, "bar", uint64(2)},
- {map[string]float64{"foo": 1.1, "bar": 2.2, "baz": 3.3}, "bar", float64(2.2)},
- {map[string]string{"foo": "FOO", "bar": "BAR", "baz": "BAZ"}, "bar", "BAR"},
- {map[string]TstX{"foo": {A: "a", B: "b"}, "bar": {A: "c", B: "d"}, "baz": {A: "e", B: "f"}}, "bar", ""},
- {(*[]string)(nil), "bar", ""},
- } {
- result := ReturnWhenSet(this.data, this.key)
- if !reflect.DeepEqual(result, this.expect) {
- t.Errorf("[%d] ReturnWhenSet got %v (type %v) but expected %v (type %v)", i, result, reflect.TypeOf(result), this.expect, reflect.TypeOf(this.expect))
- }
- }
-}
-
-func TestMarkdownify(t *testing.T) {
-
- result := Markdownify("Hello **World!**")
-
- expect := template.HTML("Hello <strong>World!</strong>")
-
- if result != expect {
- t.Errorf("Markdownify: got '%s', expected '%s'", result, expect)
- }
-}
-
-func TestApply(t *testing.T) {
- strings := []interface{}{"a\n", "b\n"}
- noStringers := []interface{}{tstNoStringer{}, tstNoStringer{}}
-
- var nilErr *error = nil
-
- chomped, _ := Apply(strings, "chomp", ".")
- assert.Equal(t, []interface{}{"a", "b"}, chomped)
-
- chomped, _ = Apply(strings, "chomp", "c\n")
- assert.Equal(t, []interface{}{"c", "c"}, chomped)
-
- chomped, _ = Apply(nil, "chomp", ".")
- assert.Equal(t, []interface{}{}, chomped)
-
- _, err := Apply(strings, "apply", ".")
- if err == nil {
- t.Errorf("apply with apply should fail")
- }
-
- _, err = Apply(nilErr, "chomp", ".")
- if err == nil {
- t.Errorf("apply with nil in seq should fail")
- }
-
- _, err = Apply(strings, "dobedobedo", ".")
- if err == nil {
- t.Errorf("apply with unknown func should fail")
- }
-
- _, err = Apply(noStringers, "chomp", ".")
- if err == nil {
- t.Errorf("apply when func fails should fail")
- }
-
- _, err = Apply(tstNoStringer{}, "chomp", ".")
- if err == nil {
- t.Errorf("apply with non-sequence should fail")
- }
-
-}
-
-func TestChomp(t *testing.T) {
- base := "\n This is\na story "
- for i, item := range []string{
- "\n", "\n\n",
- "\r", "\r\r",
- "\r\n", "\r\n\r\n",
- } {
- chomped, _ := Chomp(base + item)
-
- if chomped != base {
- t.Errorf("[%d] Chomp failed, got '%v'", i, chomped)
- }
-
- _, err := Chomp(tstNoStringer{})
-
- if err == nil {
- t.Errorf("Chomp should fail")
- }
- }
-}
-
-func TestReplace(t *testing.T) {
- v, _ := Replace("aab", "a", "b")
- assert.Equal(t, "bbb", v)
- v, _ = Replace("11a11", 1, 2)
- assert.Equal(t, "22a22", v)
- v, _ = Replace(12345, 1, 2)
- assert.Equal(t, "22345", v)
- _, e := Replace(tstNoStringer{}, "a", "b")
- assert.NotNil(t, e, "tstNoStringer isn't trimmable")
- _, e = Replace("a", tstNoStringer{}, "b")
- assert.NotNil(t, e, "tstNoStringer cannot be converted to string")
- _, e = Replace("a", "b", tstNoStringer{})
- assert.NotNil(t, e, "tstNoStringer cannot be converted to string")
-}
-
-func TestTrim(t *testing.T) {
- v, _ := Trim("1234 my way 13", "123")
- assert.Equal(t, "4 my way ", v)
- v, _ = Trim(" my way ", " ")
- assert.Equal(t, "my way", v)
- v, _ = Trim(1234, "14")
- assert.Equal(t, "23", v)
- _, e := Trim(tstNoStringer{}, " ")
- assert.NotNil(t, e, "tstNoStringer isn't trimmable")
-}
-
-func TestDateFormat(t *testing.T) {
- for i, this := range []struct {
- layout string
- value interface{}
- expect interface{}
- }{
- {"Monday, Jan 2, 2006", "2015-01-21", "Wednesday, Jan 21, 2015"},
- {"Monday, Jan 2, 2006", time.Date(2015, time.January, 21, 0, 0, 0, 0, time.UTC), "Wednesday, Jan 21, 2015"},
- {"This isn't a date layout string", "2015-01-21", "This isn't a date layout string"},
- {"Monday, Jan 2, 2006", 1421733600, false},
- {"Monday, Jan 2, 2006", 1421733600.123, false},
- } {
- result, err := DateFormat(this.layout, this.value)
- if b, ok := this.expect.(bool); ok && !b {
- if err == nil {
- t.Errorf("[%d] DateFormat didn't return an expected error", i)
- }
- } else {
- if err != nil {
- t.Errorf("[%d] DateFormat failed: %s", i, err)
- continue
- }
- if result != this.expect {
- t.Errorf("[%d] DateFormat got %v but expected %v", i, result, this.expect)
- }
- }
- }
-}
-
-func TestSafeHTML(t *testing.T) {
- for i, this := range []struct {
- str string
- tmplStr string
- expectWithoutEscape string
- expectWithEscape string
- }{
- {`<div></div>`, `{{ . }}`, `<div></div>`, `<div></div>`},
- } {
- tmpl, err := template.New("test").Parse(this.tmplStr)
- if err != nil {
- t.Errorf("[%d] unable to create new html template %q: %s", i, this.tmplStr, err)
- continue
- }
-
- buf := new(bytes.Buffer)
- err = tmpl.Execute(buf, this.str)
- if err != nil {
- t.Errorf("[%d] execute template with a raw string value returns unexpected error: %s", i, err)
- }
- if buf.String() != this.expectWithoutEscape {
- t.Errorf("[%d] execute template with a raw string value, got %v but expected %v", i, buf.String(), this.expectWithoutEscape)
- }
-
- buf.Reset()
- err = tmpl.Execute(buf, SafeHTML(this.str))
- if err != nil {
- t.Errorf("[%d] execute template with an escaped string value by SafeHTML returns unexpected error: %s", i, err)
- }
- if buf.String() != this.expectWithEscape {
- t.Errorf("[%d] execute template with an escaped string value by SafeHTML, got %v but expected %v", i, buf.String(), this.expectWithEscape)
- }
- }
-}
-
-func TestSafeHTMLAttr(t *testing.T) {
- for i, this := range []struct {
- str string
- tmplStr string
- expectWithoutEscape string
- expectWithEscape string
- }{
- {`href="irc://irc.freenode.net/#golang"`, `<a {{ . }}>irc</a>`, `<a ZgotmplZ>irc</a>`, `<a href="irc://irc.freenode.net/#golang">irc</a>`},
- } {
- tmpl, err := template.New("test").Parse(this.tmplStr)
- if err != nil {
- t.Errorf("[%d] unable to create new html template %q: %s", i, this.tmplStr, err)
- continue
- }
-
- buf := new(bytes.Buffer)
- err = tmpl.Execute(buf, this.str)
- if err != nil {
- t.Errorf("[%d] execute template with a raw string value returns unexpected error: %s", i, err)
- }
- if buf.String() != this.expectWithoutEscape {
- t.Errorf("[%d] execute template with a raw string value, got %v but expected %v", i, buf.String(), this.expectWithoutEscape)
- }
-
- buf.Reset()
- err = tmpl.Execute(buf, SafeHTMLAttr(this.str))
- if err != nil {
- t.Errorf("[%d] execute template with an escaped string value by SafeHTMLAttr returns unexpected error: %s", i, err)
- }
- if buf.String() != this.expectWithEscape {
- t.Errorf("[%d] execute template with an escaped string value by SafeHTMLAttr, got %v but expected %v", i, buf.String(), this.expectWithEscape)
- }
- }
-}
-
-func TestSafeCSS(t *testing.T) {
- for i, this := range []struct {
- str string
- tmplStr string
- expectWithoutEscape string
- expectWithEscape string
- }{
- {`width: 60px;`, `<div style="{{ . }}"></div>`, `<div style="ZgotmplZ"></div>`, `<div style="width: 60px;"></div>`},
- } {
- tmpl, err := template.New("test").Parse(this.tmplStr)
- if err != nil {
- t.Errorf("[%d] unable to create new html template %q: %s", i, this.tmplStr, err)
- continue
- }
-
- buf := new(bytes.Buffer)
- err = tmpl.Execute(buf, this.str)
- if err != nil {
- t.Errorf("[%d] execute template with a raw string value returns unexpected error: %s", i, err)
- }
- if buf.String() != this.expectWithoutEscape {
- t.Errorf("[%d] execute template with a raw string value, got %v but expected %v", i, buf.String(), this.expectWithoutEscape)
- }
-
- buf.Reset()
- err = tmpl.Execute(buf, SafeCSS(this.str))
- if err != nil {
- t.Errorf("[%d] execute template with an escaped string value by SafeCSS returns unexpected error: %s", i, err)
- }
- if buf.String() != this.expectWithEscape {
- t.Errorf("[%d] execute template with an escaped string value by SafeCSS, got %v but expected %v", i, buf.String(), this.expectWithEscape)
- }
- }
-}
-
-func TestSafeURL(t *testing.T) {
- for i, this := range []struct {
- str string
- tmplStr string
- expectWithoutEscape string
- expectWithEscape string
- }{
- {`irc://irc.freenode.net/#golang`, `<a href="{{ . }}">IRC</a>`, `<a href="#ZgotmplZ">IRC</a>`, `<a href="irc://irc.freenode.net/#golang">IRC</a>`},
- } {
- tmpl, err := template.New("test").Parse(this.tmplStr)
- if err != nil {
- t.Errorf("[%d] unable to create new html template %q: %s", i, this.tmplStr, err)
- continue
- }
-
- buf := new(bytes.Buffer)
- err = tmpl.Execute(buf, this.str)
- if err != nil {
- t.Errorf("[%d] execute template with a raw string value returns unexpected error: %s", i, err)
- }
- if buf.String() != this.expectWithoutEscape {
- t.Errorf("[%d] execute template with a raw string value, got %v but expected %v", i, buf.String(), this.expectWithoutEscape)
- }
-
- buf.Reset()
- err = tmpl.Execute(buf, SafeURL(this.str))
- if err != nil {
- t.Errorf("[%d] execute template with an escaped string value by SafeURL returns unexpected error: %s", i, err)
- }
- if buf.String() != this.expectWithEscape {
- t.Errorf("[%d] execute template with an escaped string value by SafeURL, got %v but expected %v", i, buf.String(), this.expectWithEscape)
- }
- }
-}
+// TODO(bep) test it