forked from flosch/pongo2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfilters.go
133 lines (109 loc) · 3.22 KB
/
filters.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package pongo2
import (
"fmt"
)
type FilterFunction func(in *Value, param *Value) (out *Value, err *Error)
var filters map[string]FilterFunction
func init() {
filters = make(map[string]FilterFunction)
}
// Registers a new filter. If there's already a filter with the same
// name, RegisterFilter will panic. You usually want to call this
// function in the filter's init() function:
// http://golang.org/doc/effective_go.html#init
//
// See http://www.florian-schlachter.de/post/pongo2/ for more about
// writing filters and tags.
func RegisterFilter(name string, fn FilterFunction) {
_, existing := filters[name]
if existing {
panic(fmt.Sprintf("Filter with name '%s' is already registered.", name))
}
filters[name] = fn
}
// Replaces an already registered filter with a new implementation. Use this
// function with caution since it allows you to change existing filter behaviour.
func ReplaceFilter(name string, fn FilterFunction) {
_, existing := filters[name]
if !existing {
panic(fmt.Sprintf("Filter with name '%s' does not exist (therefore cannot be overridden).", name))
}
filters[name] = fn
}
// Like ApplyFilter, but panics on an error
func MustApplyFilter(name string, value *Value, param *Value) *Value {
val, err := ApplyFilter(name, value, param)
if err != nil {
panic(err)
}
return val
}
// Applies a filter to a given value using the given parameters. Returns a *pongo2.Value or an error.
func ApplyFilter(name string, value *Value, param *Value) (*Value, *Error) {
fn, existing := filters[name]
if !existing {
return nil, &Error{
Sender: "applyfilter",
ErrorMsg: fmt.Sprintf("Filter with name '%s' not found.", name),
}
}
// Make sure param is a *Value
if param == nil {
param = AsValue(nil)
}
return fn(value, param)
}
type filterCall struct {
token *Token
name string
parameter IEvaluator
filterFunc FilterFunction
}
func (fc *filterCall) Execute(v *Value, ctx *ExecutionContext) (*Value, *Error) {
var param *Value
var err *Error
if fc.parameter != nil {
param, err = fc.parameter.Evaluate(ctx)
if err != nil {
return nil, err
}
} else {
param = AsValue(nil)
}
filtered_value, err := fc.filterFunc(v, param)
if err != nil {
return nil, err.updateFromTokenIfNeeded(ctx.template, fc.token)
}
return filtered_value, nil
}
// Filter = IDENT | IDENT ":" FilterArg | IDENT "|" Filter
func (p *Parser) parseFilter() (*filterCall, *Error) {
ident_token := p.MatchType(TokenIdentifier)
// Check filter ident
if ident_token == nil {
return nil, p.Error("Filter name must be an identifier.", nil)
}
filter := &filterCall{
token: ident_token,
name: ident_token.Val,
}
// Get the appropriate filter function and bind it
filterFn, exists := filters[ident_token.Val]
if !exists {
return nil, p.Error(fmt.Sprintf("Filter '%s' does not exist.", ident_token.Val), ident_token)
}
filter.filterFunc = filterFn
// Check for filter-argument (2 tokens needed: ':' ARG)
if p.Match(TokenSymbol, ":") != nil {
if p.Peek(TokenSymbol, "}}") != nil {
return nil, p.Error("Filter parameter required after ':'.", nil)
}
// Get filter argument expression
v, err := p.parseVariableOrLiteral()
if err != nil {
return nil, err
}
filter.parameter = v
}
return filter, nil
}