2017-06-27 00:55:12 +02:00
|
|
|
package expressions
|
|
|
|
|
|
|
|
import (
|
2017-06-27 14:46:06 +02:00
|
|
|
"encoding/json"
|
2017-06-27 00:55:12 +02:00
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
"sort"
|
|
|
|
"strings"
|
2017-06-27 14:46:06 +02:00
|
|
|
|
|
|
|
"github.com/osteele/liquid/errors"
|
2017-06-27 00:55:12 +02:00
|
|
|
)
|
|
|
|
|
2017-06-27 15:17:57 +02:00
|
|
|
type InterpreterError string
|
|
|
|
|
|
|
|
func (e InterpreterError) Error() string { return string(e) }
|
|
|
|
|
2017-06-27 00:55:12 +02:00
|
|
|
type valueFn func(Context) interface{}
|
|
|
|
|
2017-06-27 03:32:08 +02:00
|
|
|
func joinFilter(in []interface{}, sep interface{}) interface{} {
|
2017-06-27 00:55:12 +02:00
|
|
|
a := make([]string, len(in))
|
2017-06-27 03:32:08 +02:00
|
|
|
s := ", "
|
|
|
|
if sep != nil {
|
|
|
|
s = fmt.Sprint(sep)
|
|
|
|
}
|
2017-06-27 00:55:12 +02:00
|
|
|
for i, x := range in {
|
|
|
|
a[i] = fmt.Sprint(x)
|
|
|
|
}
|
2017-06-27 03:32:08 +02:00
|
|
|
return strings.Join(a, s)
|
2017-06-27 00:55:12 +02:00
|
|
|
}
|
|
|
|
|
2017-06-27 15:17:57 +02:00
|
|
|
// func sortFilter(in []interface{}, key interface{}) []interface{} {
|
|
|
|
// fmt.Println("sort", in, key)
|
2017-06-27 00:55:12 +02:00
|
|
|
func sortFilter(in []interface{}) []interface{} {
|
2017-06-27 15:17:57 +02:00
|
|
|
out := make([]interface{}, len(in))
|
|
|
|
for i, v := range in {
|
|
|
|
out[i] = v
|
2017-06-27 00:55:12 +02:00
|
|
|
}
|
2017-06-27 15:17:57 +02:00
|
|
|
sort.Sort(genericSortable(out))
|
|
|
|
return out
|
2017-06-27 00:55:12 +02:00
|
|
|
}
|
|
|
|
|
2017-06-27 03:32:08 +02:00
|
|
|
func splitFilter(in, sep string) interface{} {
|
|
|
|
return strings.Split(in, sep)
|
|
|
|
}
|
|
|
|
|
|
|
|
var filters = map[string]interface{}{}
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
DefineStandardFilters()
|
|
|
|
}
|
|
|
|
|
|
|
|
func DefineStandardFilters() {
|
2017-06-27 14:46:06 +02:00
|
|
|
// lists
|
2017-06-27 03:32:08 +02:00
|
|
|
DefineFilter("join", joinFilter)
|
|
|
|
DefineFilter("sort", sortFilter)
|
2017-06-27 14:46:06 +02:00
|
|
|
|
|
|
|
// strings
|
2017-06-27 03:32:08 +02:00
|
|
|
DefineFilter("split", splitFilter)
|
2017-06-27 14:46:06 +02:00
|
|
|
|
|
|
|
// Jekyll
|
2017-06-27 15:17:57 +02:00
|
|
|
DefineFilter("inspect", json.Marshal)
|
2017-06-27 03:32:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func DefineFilter(name string, fn interface{}) {
|
|
|
|
rf := reflect.ValueOf(fn)
|
2017-06-27 15:17:57 +02:00
|
|
|
switch {
|
|
|
|
case rf.Kind() != reflect.Func:
|
|
|
|
panic(fmt.Errorf("a filter must be a function"))
|
|
|
|
case rf.Type().NumIn() < 1:
|
|
|
|
panic(fmt.Errorf("a filter function must have at least one input"))
|
|
|
|
case rf.Type().NumOut() > 2:
|
|
|
|
panic(fmt.Errorf("a filter must be have one or two outputs"))
|
|
|
|
// case rf.Type().Out(1).Implements(…):
|
|
|
|
// panic(fmt.Errorf("a filter's second output must be type error"))
|
2017-06-27 03:32:08 +02:00
|
|
|
}
|
|
|
|
filters[name] = fn
|
2017-06-27 00:55:12 +02:00
|
|
|
}
|
|
|
|
|
2017-06-27 03:32:08 +02:00
|
|
|
func makeFilter(f valueFn, name string, param valueFn) valueFn {
|
2017-06-27 00:55:12 +02:00
|
|
|
fn, ok := filters[name]
|
|
|
|
if !ok {
|
2017-06-27 14:46:06 +02:00
|
|
|
panic(errors.UndefinedFilter(name))
|
2017-06-27 00:55:12 +02:00
|
|
|
}
|
|
|
|
fr := reflect.ValueOf(fn)
|
|
|
|
return func(ctx Context) interface{} {
|
2017-06-27 14:46:06 +02:00
|
|
|
defer func() {
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
switch e := r.(type) {
|
2017-06-27 15:17:57 +02:00
|
|
|
case genericError:
|
2017-06-27 14:46:06 +02:00
|
|
|
panic(InterpreterError(e.Error()))
|
|
|
|
default:
|
2017-06-27 15:17:57 +02:00
|
|
|
// fmt.Println(string(debug.Stack()))
|
2017-06-27 14:46:06 +02:00
|
|
|
panic(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
2017-06-27 00:55:12 +02:00
|
|
|
args := []interface{}{f(ctx)}
|
2017-06-27 03:32:08 +02:00
|
|
|
if param != nil {
|
|
|
|
args = append(args, param(ctx))
|
|
|
|
}
|
2017-06-27 00:55:12 +02:00
|
|
|
in := convertArguments(fr, args)
|
2017-06-27 15:17:57 +02:00
|
|
|
outs := fr.Call(in)
|
|
|
|
if len(outs) > 1 && outs[1].Interface() != nil {
|
|
|
|
err := outs[1].Interface()
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
switch out := outs[0].Interface().(type) {
|
|
|
|
case []byte:
|
|
|
|
return string(out)
|
|
|
|
default:
|
|
|
|
return out
|
|
|
|
}
|
2017-06-27 00:55:12 +02:00
|
|
|
}
|
|
|
|
}
|