2017-07-14 02:18:23 +02:00
|
|
|
package expressions
|
2017-06-27 00:55:12 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
2017-06-27 14:46:06 +02:00
|
|
|
|
2017-07-05 17:35:07 +02:00
|
|
|
"github.com/osteele/liquid/evaluator"
|
2017-06-27 00:55:12 +02:00
|
|
|
)
|
|
|
|
|
2017-06-27 19:36:38 +02:00
|
|
|
// An InterpreterError is an error during expression interpretation.
|
|
|
|
// It is used for errors in the input expression, to distinguish them
|
|
|
|
// from implementation errors in the interpreter.
|
2017-06-27 15:17:57 +02:00
|
|
|
type InterpreterError string
|
|
|
|
|
|
|
|
func (e InterpreterError) Error() string { return string(e) }
|
|
|
|
|
2017-06-29 13:54:31 +02:00
|
|
|
// UndefinedFilter is an error that the named filter is not defined.
|
|
|
|
type UndefinedFilter string
|
|
|
|
|
|
|
|
func (e UndefinedFilter) Error() string {
|
2017-07-14 16:17:34 +02:00
|
|
|
return fmt.Sprintf("undefined filter %q", string(e))
|
2017-06-29 13:54:31 +02:00
|
|
|
}
|
|
|
|
|
2017-06-27 00:55:12 +02:00
|
|
|
type valueFn func(Context) interface{}
|
|
|
|
|
2017-07-05 17:17:31 +02:00
|
|
|
// AddFilter adds a filter to the filter dictionary.
|
2017-07-07 11:51:31 +02:00
|
|
|
func (c *Config) AddFilter(name string, fn interface{}) {
|
2017-06-27 03:32:08 +02:00
|
|
|
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(…):
|
2017-07-05 20:24:15 +02:00
|
|
|
// panic(typeError("a filter's second output must be type error"))
|
2017-06-27 03:32:08 +02:00
|
|
|
}
|
2017-07-07 11:51:31 +02:00
|
|
|
if len(c.filters) == 0 {
|
|
|
|
c.filters = make(map[string]interface{})
|
2017-07-07 11:41:37 +02:00
|
|
|
}
|
2017-07-07 11:51:31 +02:00
|
|
|
c.filters[name] = fn
|
2017-06-27 00:55:12 +02:00
|
|
|
}
|
|
|
|
|
2017-07-05 17:17:31 +02:00
|
|
|
var closureType = reflect.TypeOf(closure{})
|
|
|
|
var interfaceType = reflect.TypeOf([]interface{}{}).Elem()
|
|
|
|
|
2017-06-28 17:24:24 +02:00
|
|
|
func isClosureInterfaceType(t reflect.Type) bool {
|
|
|
|
return closureType.ConvertibleTo(t) && !interfaceType.ConvertibleTo(t)
|
|
|
|
}
|
|
|
|
|
2017-07-05 17:17:31 +02:00
|
|
|
func (ctx *context) ApplyFilter(name string, receiver valueFn, params []valueFn) interface{} {
|
|
|
|
filter, ok := ctx.filters[name]
|
2017-06-27 00:55:12 +02:00
|
|
|
if !ok {
|
2017-06-29 13:54:31 +02:00
|
|
|
panic(UndefinedFilter(name))
|
2017-06-27 00:55:12 +02:00
|
|
|
}
|
2017-06-30 22:13:18 +02:00
|
|
|
fr := reflect.ValueOf(filter)
|
2017-07-05 17:17:31 +02:00
|
|
|
args := []interface{}{receiver(ctx)}
|
2017-06-30 22:13:18 +02:00
|
|
|
for i, param := range params {
|
|
|
|
if i+1 < fr.Type().NumIn() && isClosureInterfaceType(fr.Type().In(i+1)) {
|
|
|
|
expr, err := Parse(param(ctx).(string))
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
2017-06-28 17:24:24 +02:00
|
|
|
}
|
2017-06-30 22:13:18 +02:00
|
|
|
args = append(args, closure{expr, ctx})
|
|
|
|
} else {
|
|
|
|
args = append(args, param(ctx))
|
2017-06-27 03:32:08 +02:00
|
|
|
}
|
2017-06-30 22:13:18 +02:00
|
|
|
}
|
2017-07-05 17:35:07 +02:00
|
|
|
out, err := evaluator.Call(fr, args)
|
2017-06-30 22:13:18 +02:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2017-07-03 18:00:43 +02:00
|
|
|
out = ToLiquid(out)
|
2017-06-30 22:13:18 +02:00
|
|
|
switch out := out.(type) {
|
|
|
|
case []byte:
|
|
|
|
return string(out)
|
|
|
|
default:
|
|
|
|
return out
|
2017-06-27 00:55:12 +02:00
|
|
|
}
|
|
|
|
}
|