1
0
mirror of https://github.com/danog/liquid.git synced 2024-12-02 15:27:46 +01:00
liquid/expressions/filters.go

94 lines
2.6 KiB
Go
Raw Normal View History

2017-07-14 02:18:23 +02:00
package expressions
2017-06-27 00:55:12 +02:00
import (
"fmt"
"reflect"
2017-07-28 00:11:37 +02:00
"github.com/osteele/liquid/values"
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
}
// FilterError is the error returned by a filter when it is applied
type FilterError struct {
FilterName string
Err error
}
func (e FilterError) Error() string {
return fmt.Sprintf("error applying filter %q (%q)", e.FilterName, e.Err)
}
2017-07-28 00:11:37 +02:00
type valueFn func(Context) values.Value
2017-06-27 00:55:12 +02:00
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"))
2017-07-15 18:13:07 +02:00
case rf.Type().NumOut() < 1 || 2 < rf.Type().NumOut():
2017-06-27 15:17:57 +02:00
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()
func isClosureInterfaceType(t reflect.Type) bool {
return closureType.ConvertibleTo(t) && !interfaceType.ConvertibleTo(t)
}
2017-07-16 19:47:06 +02:00
func (ctx *context) ApplyFilter(name string, receiver valueFn, params []valueFn) (interface{}, error) {
2017-07-05 17:17:31 +02:00
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)
args := []interface{}{receiver(ctx).Interface()}
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).Interface().(string))
2017-06-30 22:13:18 +02:00
if err != nil {
panic(err)
}
2017-06-30 22:13:18 +02:00
args = append(args, closure{expr, ctx})
} else {
args = append(args, param(ctx).Interface())
2017-06-27 03:32:08 +02:00
}
2017-06-30 22:13:18 +02:00
}
2017-07-28 00:11:37 +02:00
out, err := values.Call(fr, args)
2017-06-30 22:13:18 +02:00
if err != nil {
2017-07-28 00:11:37 +02:00
if e, ok := err.(*values.CallParityError); ok {
err = &values.CallParityError{NumArgs: e.NumArgs - 1, NumParams: e.NumParams - 1}
2017-07-16 19:47:06 +02:00
}
return nil, err
2017-06-30 22:13:18 +02:00
}
switch out := out.(type) {
case []byte:
2017-07-16 19:47:06 +02:00
return string(out), nil
2017-06-30 22:13:18 +02:00
default:
2017-07-16 19:47:06 +02:00
return out, nil
2017-06-27 00:55:12 +02:00
}
}