-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnodes.go
More file actions
167 lines (149 loc) · 3.26 KB
/
Copy pathnodes.go
File metadata and controls
167 lines (149 loc) · 3.26 KB
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package vingo
import (
"fmt"
"html"
"reflect"
"strings"
)
type Node interface {
Eval(data map[string]interface{}) string
}
type TextNode struct {
Text string
}
func (n *TextNode) Eval(data map[string]interface{}) string {
return n.Text
}
type VarNode struct {
Name string
Default string
Filters []string
}
func containsFilter(filters []string, name string) bool {
for _, f := range filters {
if f == name {
return true
}
}
return false
}
func (n *VarNode) Eval(data map[string]interface{}) string {
val, ok := lookup(data, n.Name)
var out string
if ok {
out = fmt.Sprintf("%v", val)
} else if n.Default != "" {
out = n.Default
} else {
out = ""
}
// Apply filters in order
for _, f := range n.Filters {
out = applyFilter(f, out)
}
// Auto-escape unless explicitly marked raw/safe, or global AutoEscape disabled
if AutoEscape {
if !containsFilter(n.Filters, "raw") && !containsFilter(n.Filters, "safe") && !containsFilter(n.Filters, "noescape") {
out = html.EscapeString(out)
}
}
return out
}
type IfNode struct {
Branches []IfBranch
Else []Node
}
type IfBranch struct {
Expr string
Body []Node
}
func (n *IfNode) Eval(data map[string]interface{}) string {
for _, b := range n.Branches {
ok, err := evalCondition(b.Expr, data)
if err == nil && ok {
return evalNodes(b.Body, data)
}
}
// else
return evalNodes(n.Else, data)
}
type ForNode struct {
IndexVar string // optional, can be ""
ItemVar string
ListExpr string
Body []Node
}
func (n *ForNode) Eval(data map[string]interface{}) string {
seq, ok := lookup(data, n.ListExpr)
if !ok {
return ""
}
v := reflect.ValueOf(seq)
kind := v.Kind()
if kind != reflect.Slice && kind != reflect.Array {
return ""
}
length := v.Len()
out := &strings.Builder{}
for i := 0; i < length; i++ {
item := v.Index(i).Interface()
newData := shallowCopyMap(data)
if n.IndexVar != "" {
newData[n.IndexVar] = i
}
newData[n.ItemVar] = item
// loop meta
loopMeta := map[string]interface{}{
"Index": i,
"First": i == 0,
"Last": i == length-1,
"Length": length,
}
newData["loop"] = loopMeta
out.WriteString(evalNodes(n.Body, newData))
}
return out.String()
}
type SwitchNode struct {
Expr string
Cases []SwitchCase
Default []Node
}
type SwitchCase struct {
Cond string
Body []Node
}
func (n *SwitchNode) Eval(data map[string]interface{}) string {
val := lookupVal(data, n.Expr)
// Try to match with case expressions: we evaluate each case as condition:
for _, c := range n.Cases {
// if case expression is a simple literal equal to val -> match
// Alternatively evaluate case as condition using evalCondition, but allow bare literal too.
ok, err := evalConditionWithValue(c.Cond, val, data)
if err == nil && ok {
return evalNodes(c.Body, data)
}
}
// default
return evalNodes(n.Default, data)
}
// -------------------- Filters --------------------
func applyFilter(name string, input string) string {
switch name {
case "upper":
return strings.ToUpper(input)
case "lower":
return strings.ToLower(input)
case "escape":
return html.EscapeString(input)
case "raw":
// explicit raw: return as-is
return input
case "safe":
// alias for raw
return input
default:
// unknown filter: passthrough
return input
}
}