mirror of
https://github.com/gohugoio/hugo.git
synced 2025-08-31 22:41:53 +02:00
Move template library into it's own package (tpl). No longer dependent on hugolib. Can be used externally.
This commit is contained in:
714
tpl/template.go
Normal file
714
tpl/template.go
Normal file
@@ -0,0 +1,714 @@
|
||||
// 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"
|
||||
"html"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/eknkc/amber"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/spf13/hugo/helpers"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
var localTemplates *template.Template
|
||||
var tmpl Template
|
||||
|
||||
type Template interface {
|
||||
ExecuteTemplate(wr io.Writer, name string, data interface{}) error
|
||||
Lookup(name string) *template.Template
|
||||
Templates() []*template.Template
|
||||
New(name string) *template.Template
|
||||
LoadTemplates(absPath string)
|
||||
LoadTemplatesWithPrefix(absPath, prefix string)
|
||||
AddTemplate(name, tpl string) error
|
||||
AddInternalTemplate(prefix, name, tpl string) error
|
||||
AddInternalShortcode(name, tpl string) error
|
||||
}
|
||||
|
||||
type templateErr struct {
|
||||
name string
|
||||
err error
|
||||
}
|
||||
|
||||
type GoHtmlTemplate struct {
|
||||
template.Template
|
||||
errors []*templateErr
|
||||
}
|
||||
|
||||
// The "Global" Template System
|
||||
func T() Template {
|
||||
if tmpl == nil {
|
||||
tmpl = New()
|
||||
}
|
||||
|
||||
return tmpl
|
||||
}
|
||||
|
||||
// Return a new Hugo Template System
|
||||
// With all the additional features, templates & functions
|
||||
func New() Template {
|
||||
var templates = &GoHtmlTemplate{
|
||||
Template: *template.New(""),
|
||||
errors: make([]*templateErr, 0),
|
||||
}
|
||||
|
||||
localTemplates = &templates.Template
|
||||
|
||||
funcMap := template.FuncMap{
|
||||
"urlize": helpers.Urlize,
|
||||
"sanitizeurl": helpers.SanitizeUrl,
|
||||
"eq": Eq,
|
||||
"ne": Ne,
|
||||
"gt": Gt,
|
||||
"ge": Ge,
|
||||
"lt": Lt,
|
||||
"le": Le,
|
||||
"in": In,
|
||||
"intersect": Intersect,
|
||||
"isset": IsSet,
|
||||
"echoParam": ReturnWhenSet,
|
||||
"safeHtml": SafeHtml,
|
||||
"first": First,
|
||||
"where": Where,
|
||||
"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,
|
||||
}
|
||||
|
||||
templates.Funcs(funcMap)
|
||||
templates.LoadEmbedded()
|
||||
return templates
|
||||
}
|
||||
|
||||
func Eq(x, y interface{}) bool {
|
||||
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
|
||||
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, _ = strconv.ParseFloat(av.String(), 64)
|
||||
}
|
||||
|
||||
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, _ = strconv.ParseFloat(bv.String(), 64)
|
||||
}
|
||||
|
||||
return left, right
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// First is exposed to templates, to iterate over the first N items in a
|
||||
// rangeable list.
|
||||
func First(limit interface{}, seq interface{}) (interface{}, error) {
|
||||
|
||||
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)
|
||||
// this is better than my first pass; ripped from text/template/exec.go indirect():
|
||||
for ; seqv.Kind() == reflect.Ptr || seqv.Kind() == reflect.Interface; seqv = seqv.Elem() {
|
||||
if seqv.IsNil() {
|
||||
return nil, errors.New("can't iterate over a nil value")
|
||||
}
|
||||
if seqv.Kind() == reflect.Interface && seqv.NumMethod() > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func Where(seq, key, match interface{}) (interface{}, error) {
|
||||
seqv := reflect.ValueOf(seq)
|
||||
kv := reflect.ValueOf(key)
|
||||
mv := reflect.ValueOf(match)
|
||||
|
||||
// this is better than my first pass; ripped from text/template/exec.go indirect():
|
||||
for ; seqv.Kind() == reflect.Ptr || seqv.Kind() == reflect.Interface; seqv = seqv.Elem() {
|
||||
if seqv.IsNil() {
|
||||
return nil, errors.New("can't iterate over a nil value")
|
||||
}
|
||||
if seqv.Kind() == reflect.Interface && seqv.NumMethod() > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch seqv.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
r := reflect.MakeSlice(seqv.Type(), 0, 0)
|
||||
for i := 0; i < seqv.Len(); i++ {
|
||||
var vvv reflect.Value
|
||||
vv := seqv.Index(i)
|
||||
switch vv.Kind() {
|
||||
case reflect.Map:
|
||||
if kv.Type() == vv.Type().Key() && vv.MapIndex(kv).IsValid() {
|
||||
vvv = vv.MapIndex(kv)
|
||||
}
|
||||
case reflect.Struct:
|
||||
if kv.Kind() == reflect.String {
|
||||
method := vv.MethodByName(kv.String())
|
||||
if method.IsValid() && method.Type().NumIn() == 0 && method.Type().NumOut() > 0 {
|
||||
vvv = method.Call(nil)[0]
|
||||
} else if vv.FieldByName(kv.String()).IsValid() {
|
||||
vvv = vv.FieldByName(kv.String())
|
||||
}
|
||||
}
|
||||
case reflect.Ptr:
|
||||
if !vv.IsNil() {
|
||||
ev := vv.Elem()
|
||||
switch ev.Kind() {
|
||||
case reflect.Map:
|
||||
if kv.Type() == ev.Type().Key() && ev.MapIndex(kv).IsValid() {
|
||||
vvv = ev.MapIndex(kv)
|
||||
}
|
||||
case reflect.Struct:
|
||||
if kv.Kind() == reflect.String {
|
||||
method := vv.MethodByName(kv.String())
|
||||
if method.IsValid() && method.Type().NumIn() == 0 && method.Type().NumOut() > 0 {
|
||||
vvv = method.Call(nil)[0]
|
||||
} else if ev.FieldByName(kv.String()).IsValid() {
|
||||
vvv = ev.FieldByName(kv.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if vvv.IsValid() && mv.Type() == vvv.Type() {
|
||||
switch mv.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if mv.Int() == vvv.Int() {
|
||||
r = reflect.Append(r, vv)
|
||||
}
|
||||
case reflect.String:
|
||||
if mv.String() == vvv.String() {
|
||||
r = reflect.Append(r, vv)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return r.Interface(), nil
|
||||
default:
|
||||
return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())
|
||||
}
|
||||
}
|
||||
|
||||
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 interface{}, index int) interface{} {
|
||||
av := reflect.ValueOf(a)
|
||||
|
||||
switch av.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
if av.Len() > index {
|
||||
|
||||
avv := av.Index(index)
|
||||
switch avv.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return avv.Int()
|
||||
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))
|
||||
}
|
||||
|
||||
func SafeHtml(text string) template.HTML {
|
||||
return template.HTML(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:]
|
||||
}
|
||||
var context interface{}
|
||||
|
||||
if len(context_list) == 0 {
|
||||
context = nil
|
||||
} else {
|
||||
context = context_list[0]
|
||||
}
|
||||
return ExecuteTemplateToHTML(context, "partials/"+name, "theme/partials/"+name)
|
||||
}
|
||||
|
||||
func ExecuteTemplate(context interface{}, layouts ...string) *bytes.Buffer {
|
||||
buffer := new(bytes.Buffer)
|
||||
worked := false
|
||||
for _, layout := range layouts {
|
||||
name := layout
|
||||
|
||||
if localTemplates.Lookup(name) == nil {
|
||||
name = layout + ".html"
|
||||
}
|
||||
|
||||
if localTemplates.Lookup(name) != nil {
|
||||
err := localTemplates.ExecuteTemplate(buffer, name, context)
|
||||
if err != nil {
|
||||
jww.ERROR.Println(err, "in", name)
|
||||
}
|
||||
worked = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !worked {
|
||||
jww.ERROR.Println("Unable to render", layouts)
|
||||
jww.ERROR.Println("Expecting to find a template in either the theme/layouts or /layouts in one of the following relative locations", layouts)
|
||||
}
|
||||
|
||||
return buffer
|
||||
}
|
||||
|
||||
func ExecuteTemplateToHTML(context interface{}, layouts ...string) template.HTML {
|
||||
b := ExecuteTemplate(context, layouts...)
|
||||
return template.HTML(string(b.Bytes()))
|
||||
}
|
||||
|
||||
func (t *GoHtmlTemplate) LoadEmbedded() {
|
||||
t.EmbedShortcodes()
|
||||
t.EmbedTemplates()
|
||||
}
|
||||
|
||||
func (t *GoHtmlTemplate) AddInternalTemplate(prefix, name, tpl string) error {
|
||||
if prefix != "" {
|
||||
return t.AddTemplate("_internal/"+prefix+"/"+name, tpl)
|
||||
} else {
|
||||
return t.AddTemplate("_internal/"+name, tpl)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *GoHtmlTemplate) AddInternalShortcode(name, content string) error {
|
||||
return t.AddInternalTemplate("shortcodes", name, content)
|
||||
}
|
||||
|
||||
func (t *GoHtmlTemplate) AddTemplate(name, tpl string) error {
|
||||
_, err := t.New(name).Parse(tpl)
|
||||
if err != nil {
|
||||
t.errors = append(t.errors, &templateErr{name: name, err: err})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *GoHtmlTemplate) AddTemplateFile(name, path string) error {
|
||||
// get the suffix and switch on that
|
||||
ext := filepath.Ext(path)
|
||||
switch ext {
|
||||
case ".amber":
|
||||
compiler := amber.New()
|
||||
// Parse the input file
|
||||
if err := compiler.ParseFile(path); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := compiler.CompileWithTemplate(t.New(name)); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return t.AddTemplate(name, string(b))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (t *GoHtmlTemplate) generateTemplateNameFrom(base, path string) string {
|
||||
return filepath.ToSlash(path[len(base)+1:])
|
||||
}
|
||||
|
||||
func ignoreDotFile(path string) bool {
|
||||
return filepath.Base(path)[0] == '.'
|
||||
}
|
||||
|
||||
func (t *GoHtmlTemplate) loadTemplates(absPath string, prefix string) {
|
||||
walker := func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !fi.IsDir() {
|
||||
if ignoreDotFile(path) {
|
||||
return nil
|
||||
}
|
||||
|
||||
tplName := t.generateTemplateNameFrom(absPath, path)
|
||||
|
||||
if prefix != "" {
|
||||
tplName = strings.Trim(prefix, "/") + "/" + tplName
|
||||
}
|
||||
|
||||
t.AddTemplateFile(tplName, path)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
filepath.Walk(absPath, walker)
|
||||
}
|
||||
|
||||
func (t *GoHtmlTemplate) LoadTemplatesWithPrefix(absPath string, prefix string) {
|
||||
t.loadTemplates(absPath, prefix)
|
||||
}
|
||||
|
||||
func (t *GoHtmlTemplate) LoadTemplates(absPath string) {
|
||||
t.loadTemplates(absPath, "")
|
||||
}
|
97
tpl/template_embedded.go
Normal file
97
tpl/template_embedded.go
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright © 2013 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
|
||||
|
||||
type Tmpl struct {
|
||||
Name string
|
||||
Data string
|
||||
}
|
||||
|
||||
func (t *GoHtmlTemplate) EmbedShortcodes() {
|
||||
t.AddInternalShortcode("highlight.html", `{{ .Get 0 | highlight .Inner }}`)
|
||||
t.AddInternalShortcode("test.html", `This is a simple Test`)
|
||||
t.AddInternalShortcode("figure.html", `<!-- image -->
|
||||
<figure {{ with .Get "class" }}class="{{.}}"{{ end }}>
|
||||
{{ with .Get "link"}}<a href="{{.}}">{{ end }}
|
||||
<img src="{{ .Get "src" }}" {{ if or (.Get "alt") (.Get "caption") }}alt="{{ with .Get "alt"}}{{.}}{{else}}{{ .Get "caption" }}{{ end }}" {{ end }}{{ with .Get "width" }}width="{{.}}" {{ end }}/>
|
||||
{{ if .Get "link"}}</a>{{ end }}
|
||||
{{ if or (or (.Get "title") (.Get "caption")) (.Get "attr")}}
|
||||
<figcaption>{{ if isset .Params "title" }}
|
||||
<h4>{{ .Get "title" }}</h4>{{ end }}
|
||||
{{ if or (.Get "caption") (.Get "attr")}}<p>
|
||||
{{ .Get "caption" }}
|
||||
{{ with .Get "attrlink"}}<a href="{{.}}"> {{ end }}
|
||||
{{ .Get "attr" }}
|
||||
{{ if .Get "attrlink"}}</a> {{ end }}
|
||||
</p> {{ end }}
|
||||
</figcaption>
|
||||
{{ end }}
|
||||
</figure>
|
||||
<!-- image -->`)
|
||||
}
|
||||
|
||||
func (t *GoHtmlTemplate) EmbedTemplates() {
|
||||
|
||||
t.AddInternalTemplate("_default", "rss.xml", `<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}</title>
|
||||
<link>{{ .Permalink }}</link>
|
||||
<description>Recent content {{ with .Title }}in {{.}} {{ end }}on {{ .Site.Title }}</description>
|
||||
<generator>Hugo -- gohugo.io</generator>
|
||||
{{ with .Site.LanguageCode }}<language>{{.}}</language>{{end}}
|
||||
{{ with .Site.Author.name }}<author>{{.}}</author>{{end}}
|
||||
{{ with .Site.Copyright }}<copyright>{{.}}</copyright>{{end}}
|
||||
<lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 MST" }}</lastBuildDate>
|
||||
<atom:link href="{{.Url}}" rel="self" type="application/rss+xml" />
|
||||
{{ range first 15 .Data.Pages }}
|
||||
<item>
|
||||
<title>{{ .Title }}</title>
|
||||
<link>{{ .Permalink }}</link>
|
||||
<pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 MST" }}</pubDate>
|
||||
{{with .Site.Author.name}}<author>{{.}}</author>{{end}}
|
||||
<guid>{{ .Permalink }}</guid>
|
||||
<description>{{ .Content | html }}</description>
|
||||
</item>
|
||||
{{ end }}
|
||||
</channel>
|
||||
</rss>`)
|
||||
|
||||
t.AddInternalTemplate("_default", "sitemap.xml", `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
{{ range .Data.Pages }}
|
||||
<url>
|
||||
<loc>{{ .Permalink }}</loc>
|
||||
<lastmod>{{ safeHtml ( .Date.Format "2006-01-02T15:04:05-07:00" ) }}</lastmod>{{ with .Sitemap.ChangeFreq }}
|
||||
<changefreq>{{ . }}</changefreq>{{ end }}{{ if ge .Sitemap.Priority 0.0 }}
|
||||
<priority>{{ .Sitemap.Priority }}</priority>{{ end }}
|
||||
</url>
|
||||
{{ end }}
|
||||
</urlset>`)
|
||||
|
||||
t.AddInternalTemplate("", "disqus.html", `{{ if .Site.DisqusShortname }}<div id="disqus_thread"></div>
|
||||
<script type="text/javascript">
|
||||
var disqus_shortname = '{{ .Site.DisqusShortname }}';
|
||||
var disqus_identifier = '{{with .GetParam "disqus_identifier" }}{{ . }}{{ else }}{{ .Permalink }}{{end}}';
|
||||
var disqus_title = '{{with .GetParam "disqus_title" }}{{ . }}{{ else }}{{ .Title }}{{end}}';
|
||||
var disqus_url = '{{with .GetParam "disqus_url" }}{{ . | html }}{{ else }}{{ .Permalink }}{{end}}';
|
||||
|
||||
(function() {
|
||||
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
|
||||
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
|
||||
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
|
||||
})();
|
||||
</script>
|
||||
<noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
|
||||
<a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>{{end}}`)
|
||||
|
||||
}
|
341
tpl/template_test.go
Normal file
341
tpl/template_test.go
Normal file
@@ -0,0 +1,341 @@
|
||||
package tpl
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGt(t *testing.T) {
|
||||
for i, this := range []struct {
|
||||
left interface{}
|
||||
right interface{}
|
||||
leftShouldWin bool
|
||||
}{
|
||||
{5, 8, false},
|
||||
{8, 5, true},
|
||||
{5, 5, false},
|
||||
{-2, 1, false},
|
||||
{2, -5, true},
|
||||
{0.0, 1.23, false},
|
||||
{1.23, 0.0, true},
|
||||
{"8", "5", true},
|
||||
{"5", "0001", true},
|
||||
{[]int{100, 99}, []int{1, 2, 3, 4}, false},
|
||||
} {
|
||||
leftIsBigger := Gt(this.left, this.right)
|
||||
if leftIsBigger != this.leftShouldWin {
|
||||
var which string
|
||||
if leftIsBigger {
|
||||
which = "expected right to be bigger, but left was"
|
||||
} else {
|
||||
which = "expected left to be bigger, but right was"
|
||||
}
|
||||
t.Errorf("[%d] %v compared to %v: %s", i, this.left, this.right, which)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoArithmetic(t *testing.T) {
|
||||
for i, this := range []struct {
|
||||
a interface{}
|
||||
b interface{}
|
||||
op rune
|
||||
expect interface{}
|
||||
}{
|
||||
{3, 2, '+', int64(5)},
|
||||
{3, 2, '-', int64(1)},
|
||||
{3, 2, '*', int64(6)},
|
||||
{3, 2, '/', int64(1)},
|
||||
{3.0, 2, '+', float64(5)},
|
||||
{3.0, 2, '-', float64(1)},
|
||||
{3.0, 2, '*', float64(6)},
|
||||
{3.0, 2, '/', float64(1.5)},
|
||||
{3, 2.0, '+', float64(5)},
|
||||
{3, 2.0, '-', float64(1)},
|
||||
{3, 2.0, '*', float64(6)},
|
||||
{3, 2.0, '/', float64(1.5)},
|
||||
{3.0, 2.0, '+', float64(5)},
|
||||
{3.0, 2.0, '-', float64(1)},
|
||||
{3.0, 2.0, '*', float64(6)},
|
||||
{3.0, 2.0, '/', float64(1.5)},
|
||||
{uint(3), uint(2), '+', uint64(5)},
|
||||
{uint(3), uint(2), '-', uint64(1)},
|
||||
{uint(3), uint(2), '*', uint64(6)},
|
||||
{uint(3), uint(2), '/', uint64(1)},
|
||||
{uint(3), 2, '+', uint64(5)},
|
||||
{uint(3), 2, '-', uint64(1)},
|
||||
{uint(3), 2, '*', uint64(6)},
|
||||
{uint(3), 2, '/', uint64(1)},
|
||||
{3, uint(2), '+', uint64(5)},
|
||||
{3, uint(2), '-', uint64(1)},
|
||||
{3, uint(2), '*', uint64(6)},
|
||||
{3, uint(2), '/', uint64(1)},
|
||||
{uint(3), -2, '+', int64(1)},
|
||||
{uint(3), -2, '-', int64(5)},
|
||||
{uint(3), -2, '*', int64(-6)},
|
||||
{uint(3), -2, '/', int64(-1)},
|
||||
{-3, uint(2), '+', int64(-1)},
|
||||
{-3, uint(2), '-', int64(-5)},
|
||||
{-3, uint(2), '*', int64(-6)},
|
||||
{-3, uint(2), '/', int64(-1)},
|
||||
{uint(3), 2.0, '+', float64(5)},
|
||||
{uint(3), 2.0, '-', float64(1)},
|
||||
{uint(3), 2.0, '*', float64(6)},
|
||||
{uint(3), 2.0, '/', float64(1.5)},
|
||||
{3.0, uint(2), '+', float64(5)},
|
||||
{3.0, uint(2), '-', float64(1)},
|
||||
{3.0, uint(2), '*', float64(6)},
|
||||
{3.0, uint(2), '/', float64(1.5)},
|
||||
{0, 0, '+', 0},
|
||||
{0, 0, '-', 0},
|
||||
{0, 0, '*', 0},
|
||||
{"foo", "bar", '+', "foobar"},
|
||||
{3, 0, '/', false},
|
||||
{3.0, 0, '/', false},
|
||||
{3, 0.0, '/', false},
|
||||
{uint(3), uint(0), '/', false},
|
||||
{3, uint(0), '/', false},
|
||||
{-3, uint(0), '/', false},
|
||||
{uint(3), 0, '/', false},
|
||||
{3.0, uint(0), '/', false},
|
||||
{uint(3), 0.0, '/', false},
|
||||
{3, "foo", '+', false},
|
||||
{3.0, "foo", '+', false},
|
||||
{uint(3), "foo", '+', false},
|
||||
{"foo", 3, '+', false},
|
||||
{"foo", "bar", '-', false},
|
||||
{3, 2, '%', false},
|
||||
} {
|
||||
result, err := doArithmetic(this.a, this.b, this.op)
|
||||
if b, ok := this.expect.(bool); ok && !b {
|
||||
if err == nil {
|
||||
t.Errorf("[%d] doArithmetic didn't return an expected error")
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("[%d] failed: %s", i, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(result, this.expect) {
|
||||
t.Errorf("[%d] doArithmetic got %v but expected %v", i, result, 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")
|
||||
}
|
||||
} 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")
|
||||
}
|
||||
} 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},
|
||||
} {
|
||||
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")
|
||||
}
|
||||
} 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 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 (x *TstX) TstRp() string {
|
||||
return "r" + x.A
|
||||
}
|
||||
|
||||
func (x TstX) TstRv() string {
|
||||
return "r" + x.B
|
||||
}
|
||||
|
||||
type TstX struct {
|
||||
A, B string
|
||||
}
|
||||
|
||||
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")}}
|
||||
|
||||
for i, this := range []struct {
|
||||
sequence interface{}
|
||||
key interface{}
|
||||
match interface{}
|
||||
expect interface{}
|
||||
}{
|
||||
{[]map[int]string{{1: "a", 2: "m"}, {1: "c", 2: "d"}, {1: "e", 3: "m"}}, 2, "m", []map[int]string{{1: "a", 2: "m"}}},
|
||||
{[]map[string]int{{"a": 1, "b": 2}, {"a": 3, "b": 4}, {"a": 5, "x": 4}}, "b", 4, []map[string]int{{"a": 3, "b": 4}}},
|
||||
{[]TstX{{"a", "b"}, {"c", "d"}, {"e", "f"}}, "B", "f", []TstX{{"e", "f"}}},
|
||||
{[]*map[int]string{&map[int]string{1: "a", 2: "m"}, &map[int]string{1: "c", 2: "d"}, &map[int]string{1: "e", 3: "m"}}, 2, "m", []*map[int]string{&map[int]string{1: "a", 2: "m"}}},
|
||||
{[]*TstX{&TstX{"a", "b"}, &TstX{"c", "d"}, &TstX{"e", "f"}}, "B", "f", []*TstX{&TstX{"e", "f"}}},
|
||||
{[]*TstX{&TstX{"a", "b"}, &TstX{"c", "d"}, &TstX{"e", "c"}}, "TstRp", "rc", []*TstX{&TstX{"c", "d"}}},
|
||||
{[]TstX{TstX{"a", "b"}, TstX{"c", "d"}, TstX{"e", "c"}}, "TstRv", "rc", []TstX{TstX{"e", "c"}}},
|
||||
//{[]*Page{page1, page2}, "Type", "v", []*Page{page1}},
|
||||
//{[]*Page{page1, page2}, "Section", "y", []*Page{page2}},
|
||||
} {
|
||||
results, err := Where(this.sequence, this.key, this.match)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user