diff options
Diffstat (limited to 'vendor/github.com/hashicorp/hcl2/hclwrite/generate.go')
-rw-r--r-- | vendor/github.com/hashicorp/hcl2/hclwrite/generate.go | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/hcl2/hclwrite/generate.go b/vendor/github.com/hashicorp/hcl2/hclwrite/generate.go new file mode 100644 index 0000000..d249cfd --- /dev/null +++ b/vendor/github.com/hashicorp/hcl2/hclwrite/generate.go | |||
@@ -0,0 +1,250 @@ | |||
1 | package hclwrite | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "unicode" | ||
6 | "unicode/utf8" | ||
7 | |||
8 | "github.com/hashicorp/hcl2/hcl" | ||
9 | "github.com/hashicorp/hcl2/hcl/hclsyntax" | ||
10 | "github.com/zclconf/go-cty/cty" | ||
11 | ) | ||
12 | |||
13 | // TokensForValue returns a sequence of tokens that represents the given | ||
14 | // constant value. | ||
15 | // | ||
16 | // This function only supports types that are used by HCL. In particular, it | ||
17 | // does not support capsule types and will panic if given one. | ||
18 | // | ||
19 | // It is not possible to express an unknown value in source code, so this | ||
20 | // function will panic if the given value is unknown or contains any unknown | ||
21 | // values. A caller can call the value's IsWhollyKnown method to verify that | ||
22 | // no unknown values are present before calling TokensForValue. | ||
23 | func TokensForValue(val cty.Value) Tokens { | ||
24 | toks := appendTokensForValue(val, nil) | ||
25 | format(toks) // fiddle with the SpacesBefore field to get canonical spacing | ||
26 | return toks | ||
27 | } | ||
28 | |||
29 | // TokensForTraversal returns a sequence of tokens that represents the given | ||
30 | // traversal. | ||
31 | // | ||
32 | // If the traversal is absolute then the result is a self-contained, valid | ||
33 | // reference expression. If the traversal is relative then the returned tokens | ||
34 | // could be appended to some other expression tokens to traverse into the | ||
35 | // represented expression. | ||
36 | func TokensForTraversal(traversal hcl.Traversal) Tokens { | ||
37 | toks := appendTokensForTraversal(traversal, nil) | ||
38 | format(toks) // fiddle with the SpacesBefore field to get canonical spacing | ||
39 | return toks | ||
40 | } | ||
41 | |||
42 | func appendTokensForValue(val cty.Value, toks Tokens) Tokens { | ||
43 | switch { | ||
44 | |||
45 | case !val.IsKnown(): | ||
46 | panic("cannot produce tokens for unknown value") | ||
47 | |||
48 | case val.IsNull(): | ||
49 | toks = append(toks, &Token{ | ||
50 | Type: hclsyntax.TokenIdent, | ||
51 | Bytes: []byte(`null`), | ||
52 | }) | ||
53 | |||
54 | case val.Type() == cty.Bool: | ||
55 | var src []byte | ||
56 | if val.True() { | ||
57 | src = []byte(`true`) | ||
58 | } else { | ||
59 | src = []byte(`false`) | ||
60 | } | ||
61 | toks = append(toks, &Token{ | ||
62 | Type: hclsyntax.TokenIdent, | ||
63 | Bytes: src, | ||
64 | }) | ||
65 | |||
66 | case val.Type() == cty.Number: | ||
67 | bf := val.AsBigFloat() | ||
68 | srcStr := bf.Text('f', -1) | ||
69 | toks = append(toks, &Token{ | ||
70 | Type: hclsyntax.TokenNumberLit, | ||
71 | Bytes: []byte(srcStr), | ||
72 | }) | ||
73 | |||
74 | case val.Type() == cty.String: | ||
75 | // TODO: If it's a multi-line string ending in a newline, format | ||
76 | // it as a HEREDOC instead. | ||
77 | src := escapeQuotedStringLit(val.AsString()) | ||
78 | toks = append(toks, &Token{ | ||
79 | Type: hclsyntax.TokenOQuote, | ||
80 | Bytes: []byte{'"'}, | ||
81 | }) | ||
82 | if len(src) > 0 { | ||
83 | toks = append(toks, &Token{ | ||
84 | Type: hclsyntax.TokenQuotedLit, | ||
85 | Bytes: src, | ||
86 | }) | ||
87 | } | ||
88 | toks = append(toks, &Token{ | ||
89 | Type: hclsyntax.TokenCQuote, | ||
90 | Bytes: []byte{'"'}, | ||
91 | }) | ||
92 | |||
93 | case val.Type().IsListType() || val.Type().IsSetType() || val.Type().IsTupleType(): | ||
94 | toks = append(toks, &Token{ | ||
95 | Type: hclsyntax.TokenOBrack, | ||
96 | Bytes: []byte{'['}, | ||
97 | }) | ||
98 | |||
99 | i := 0 | ||
100 | for it := val.ElementIterator(); it.Next(); { | ||
101 | if i > 0 { | ||
102 | toks = append(toks, &Token{ | ||
103 | Type: hclsyntax.TokenComma, | ||
104 | Bytes: []byte{','}, | ||
105 | }) | ||
106 | } | ||
107 | _, eVal := it.Element() | ||
108 | toks = appendTokensForValue(eVal, toks) | ||
109 | i++ | ||
110 | } | ||
111 | |||
112 | toks = append(toks, &Token{ | ||
113 | Type: hclsyntax.TokenCBrack, | ||
114 | Bytes: []byte{']'}, | ||
115 | }) | ||
116 | |||
117 | case val.Type().IsMapType() || val.Type().IsObjectType(): | ||
118 | toks = append(toks, &Token{ | ||
119 | Type: hclsyntax.TokenOBrace, | ||
120 | Bytes: []byte{'{'}, | ||
121 | }) | ||
122 | |||
123 | i := 0 | ||
124 | for it := val.ElementIterator(); it.Next(); { | ||
125 | if i > 0 { | ||
126 | toks = append(toks, &Token{ | ||
127 | Type: hclsyntax.TokenComma, | ||
128 | Bytes: []byte{','}, | ||
129 | }) | ||
130 | } | ||
131 | eKey, eVal := it.Element() | ||
132 | if hclsyntax.ValidIdentifier(eKey.AsString()) { | ||
133 | toks = append(toks, &Token{ | ||
134 | Type: hclsyntax.TokenIdent, | ||
135 | Bytes: []byte(eKey.AsString()), | ||
136 | }) | ||
137 | } else { | ||
138 | toks = appendTokensForValue(eKey, toks) | ||
139 | } | ||
140 | toks = append(toks, &Token{ | ||
141 | Type: hclsyntax.TokenEqual, | ||
142 | Bytes: []byte{'='}, | ||
143 | }) | ||
144 | toks = appendTokensForValue(eVal, toks) | ||
145 | i++ | ||
146 | } | ||
147 | |||
148 | toks = append(toks, &Token{ | ||
149 | Type: hclsyntax.TokenCBrace, | ||
150 | Bytes: []byte{'}'}, | ||
151 | }) | ||
152 | |||
153 | default: | ||
154 | panic(fmt.Sprintf("cannot produce tokens for %#v", val)) | ||
155 | } | ||
156 | |||
157 | return toks | ||
158 | } | ||
159 | |||
160 | func appendTokensForTraversal(traversal hcl.Traversal, toks Tokens) Tokens { | ||
161 | for _, step := range traversal { | ||
162 | appendTokensForTraversalStep(step, toks) | ||
163 | } | ||
164 | return toks | ||
165 | } | ||
166 | |||
167 | func appendTokensForTraversalStep(step hcl.Traverser, toks Tokens) { | ||
168 | switch ts := step.(type) { | ||
169 | case hcl.TraverseRoot: | ||
170 | toks = append(toks, &Token{ | ||
171 | Type: hclsyntax.TokenIdent, | ||
172 | Bytes: []byte(ts.Name), | ||
173 | }) | ||
174 | case hcl.TraverseAttr: | ||
175 | toks = append( | ||
176 | toks, | ||
177 | &Token{ | ||
178 | Type: hclsyntax.TokenDot, | ||
179 | Bytes: []byte{'.'}, | ||
180 | }, | ||
181 | &Token{ | ||
182 | Type: hclsyntax.TokenIdent, | ||
183 | Bytes: []byte(ts.Name), | ||
184 | }, | ||
185 | ) | ||
186 | case hcl.TraverseIndex: | ||
187 | toks = append(toks, &Token{ | ||
188 | Type: hclsyntax.TokenOBrack, | ||
189 | Bytes: []byte{'['}, | ||
190 | }) | ||
191 | appendTokensForValue(ts.Key, toks) | ||
192 | toks = append(toks, &Token{ | ||
193 | Type: hclsyntax.TokenCBrack, | ||
194 | Bytes: []byte{']'}, | ||
195 | }) | ||
196 | default: | ||
197 | panic(fmt.Sprintf("unsupported traversal step type %T", step)) | ||
198 | } | ||
199 | } | ||
200 | |||
201 | func escapeQuotedStringLit(s string) []byte { | ||
202 | if len(s) == 0 { | ||
203 | return nil | ||
204 | } | ||
205 | buf := make([]byte, 0, len(s)) | ||
206 | for i, r := range s { | ||
207 | switch r { | ||
208 | case '\n': | ||
209 | buf = append(buf, '\\', 'n') | ||
210 | case '\r': | ||
211 | buf = append(buf, '\\', 'r') | ||
212 | case '\t': | ||
213 | buf = append(buf, '\\', 't') | ||
214 | case '"': | ||
215 | buf = append(buf, '\\', '"') | ||
216 | case '\\': | ||
217 | buf = append(buf, '\\', '\\') | ||
218 | case '$', '%': | ||
219 | buf = appendRune(buf, r) | ||
220 | remain := s[i+1:] | ||
221 | if len(remain) > 0 && remain[0] == '{' { | ||
222 | // Double up our template introducer symbol to escape it. | ||
223 | buf = appendRune(buf, r) | ||
224 | } | ||
225 | default: | ||
226 | if !unicode.IsPrint(r) { | ||
227 | var fmted string | ||
228 | if r < 65536 { | ||
229 | fmted = fmt.Sprintf("\\u%04x", r) | ||
230 | } else { | ||
231 | fmted = fmt.Sprintf("\\U%08x", r) | ||
232 | } | ||
233 | buf = append(buf, fmted...) | ||
234 | } else { | ||
235 | buf = appendRune(buf, r) | ||
236 | } | ||
237 | } | ||
238 | } | ||
239 | return buf | ||
240 | } | ||
241 | |||
242 | func appendRune(b []byte, r rune) []byte { | ||
243 | l := utf8.RuneLen(r) | ||
244 | for i := 0; i < l; i++ { | ||
245 | b = append(b, 0) // make room at the end of our buffer | ||
246 | } | ||
247 | ch := b[len(b)-l:] | ||
248 | utf8.EncodeRune(ch, r) | ||
249 | return b | ||
250 | } | ||