ref: be2097e1ad789eca5d893805a059d94defbe5c48
parent: 42dcaabf4f81ccd8aa831049440bb0c5d85707d6
author: bep <bjorn.erik.pedersen@gmail.com>
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 <spf@spf13.com>.
+//
+// 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
--
⑨