...
1 package envsubst
2
3 import (
4 "bytes"
5 "io"
6 "io/ioutil"
7
8 "github.com/drone/envsubst/v2/parse"
9 )
10
11
12
13 type state struct {
14 template *Template
15 writer io.Writer
16 node parse.Node
17
18
19 mapper func(string) string
20 }
21
22
23 type Template struct {
24 tree *parse.Tree
25 }
26
27
28
29 func Parse(s string) (t *Template, err error) {
30 t = new(Template)
31 t.tree, err = parse.Parse(s)
32 if err != nil {
33 return nil, err
34 }
35 return t, nil
36 }
37
38
39
40 func ParseFile(path string) (*Template, error) {
41 b, err := ioutil.ReadFile(path)
42 if err != nil {
43 return nil, err
44 }
45 return Parse(string(b))
46 }
47
48
49 func (t *Template) Execute(mapping func(string) string) (str string, err error) {
50 b := new(bytes.Buffer)
51 s := new(state)
52 s.node = t.tree.Root
53 s.mapper = mapping
54 s.writer = b
55 err = t.eval(s)
56 if err != nil {
57 return
58 }
59 return b.String(), nil
60 }
61
62 func (t *Template) eval(s *state) (err error) {
63 switch node := s.node.(type) {
64 case *parse.TextNode:
65 err = t.evalText(s, node)
66 case *parse.FuncNode:
67 err = t.evalFunc(s, node)
68 case *parse.ListNode:
69 err = t.evalList(s, node)
70 }
71 return err
72 }
73
74 func (t *Template) evalText(s *state, node *parse.TextNode) error {
75 _, err := io.WriteString(s.writer, node.Value)
76 return err
77 }
78
79 func (t *Template) evalList(s *state, node *parse.ListNode) (err error) {
80 for _, n := range node.Nodes {
81 s.node = n
82 err = t.eval(s)
83 if err != nil {
84 return err
85 }
86 }
87 return nil
88 }
89
90 func (t *Template) evalFunc(s *state, node *parse.FuncNode) error {
91 var w = s.writer
92 var buf bytes.Buffer
93 var args []string
94 for _, n := range node.Args {
95 buf.Reset()
96 s.writer = &buf
97 s.node = n
98 err := t.eval(s)
99 if err != nil {
100 return err
101 }
102 args = append(args, buf.String())
103 }
104
105
106 s.writer = w
107 s.node = node
108
109 v := s.mapper(node.Param)
110
111 fn := lookupFunc(node.Name, len(args))
112
113 _, err := io.WriteString(s.writer, fn(v, args...))
114 return err
115 }
116
117
118
119 func lookupFunc(name string, args int) substituteFunc {
120 switch name {
121 case ",":
122 return toLowerFirst
123 case ",,":
124 return toLower
125 case "^":
126 return toUpperFirst
127 case "^^":
128 return toUpper
129 case "#":
130 if args == 0 {
131 return toLen
132 }
133 return trimShortestPrefix
134 case "##":
135 return trimLongestPrefix
136 case "%":
137 return trimShortestSuffix
138 case "%%":
139 return trimLongestSuffix
140 case ":":
141 return toSubstr
142 case "/#":
143 return replacePrefix
144 case "/%":
145 return replaceSuffix
146 case "/":
147 return replaceFirst
148 case "//":
149 return replaceAll
150 case "=", ":=", ":-":
151 return toDefault
152 case ":?", ":+", "-", "+":
153 return toDefault
154 default:
155 return toDefault
156 }
157 }
158
View as plain text