generated from atomicgo/template
/
parser.go
103 lines (82 loc) 路 2.04 KB
/
parser.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
package f
import (
"fmt"
"regexp"
"strings"
"github.com/antonmedv/expr"
)
// Parsed contains a parsed template string, ready to be evaluated.
type Parsed struct {
Template string
Parts []Part
}
// Parse parses a template string into a Parsed struct.
func Parse(template string) Parsed {
return Parsed{
Template: template,
Parts: splitIntoParts(template),
}
}
// String returns the parsed template parts as a single string.
func (parsed Parsed) String() string {
var res strings.Builder
for _, part := range parsed.Parts {
res.WriteString(part.Value)
}
return res.String()
}
// Eval evaluated expressions in the parsed template string.
func (parsed Parsed) Eval(data any) (string, error) {
for i, part := range parsed.Parts {
if part.Parsed {
continue
}
exp, err := expr.Compile(part.Value, expr.Env(data))
if err != nil {
return "", fmt.Errorf("failed to compile expression: %w", err)
}
result, err := expr.Run(exp, data)
if err != nil {
return "", fmt.Errorf("failed to evaluate expression: %w", err)
}
parsed.Parts[i].Value = fmt.Sprintf("%v", result)
}
return parsed.String(), nil
}
// Part is a single part of a template string.
// Can either be a raw string, or an expression.
type Part struct {
Value string
Parsed bool
}
func splitIntoParts(template string) []Part {
r := regexp.MustCompile(`\$\{([^\}]*)\}`)
matches := r.FindAllStringSubmatchIndex(template, -1)
parts := make([]Part, 0, len(matches)+1)
lastIndex := 0
for _, match := range matches {
start, end := match[0], match[1]
exprStart, exprEnd := match[2], match[3]
// Add the string before the match, if any
if start > lastIndex {
parts = append(parts, Part{
Value: template[lastIndex:start],
Parsed: true,
})
}
// Add the expression
parts = append(parts, Part{
Value: template[exprStart:exprEnd],
Parsed: false,
})
lastIndex = end
}
// Add remaining string, if any
if lastIndex < len(template) {
parts = append(parts, Part{
Value: template[lastIndex:],
Parsed: true,
})
}
return parts
}