]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/hcl2/hclwrite/generate.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / hcl2 / hclwrite / generate.go
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 }