@@ -3,12 +3,9 @@ package hclencoder
33import (
44 "errors"
55 "fmt"
6- "github.com/hashicorp/hcl/v2"
7- "github.com/hashicorp/hcl/v2/hclsyntax"
86 "github.com/hashicorp/hcl/v2/hclwrite"
97 "github.com/zclconf/go-cty/cty"
108 "reflect"
11- "sort"
129 "strings"
1310)
1411
@@ -70,12 +67,35 @@ type fieldMeta struct {
7067 omitEmpty bool
7168}
7269
73- func encode (in reflect.Value ) (node * Node , err error ) {
70+ type node struct {
71+ Block * hclwrite.Block
72+ BlockList []* hclwrite.Block
73+ Value * cty.Value
74+ Tokens hclwrite.Tokens
75+ }
76+
77+ func (n node ) isValue () bool {
78+ return n .Value != nil
79+ }
80+
81+ func (n node ) isBlock () bool {
82+ return n .Block != nil
83+ }
84+
85+ func (n node ) isBlockList () bool {
86+ return n .BlockList != nil
87+ }
88+
89+ func (n node ) isTokens () bool {
90+ return n .Tokens != nil
91+ }
92+
93+ func encode (in reflect.Value ) (node * node , err error ) {
7494 return encodeField (in , fieldMeta {})
7595}
7696
77- // encode converts a reflected valued into an HCL ast.Node in a depth-first manner.
78- func encodeField (in reflect.Value , meta fieldMeta ) (node * Node , err error ) {
97+ // encode converts a reflected valued into an HCL ast.node in a depth-first manner.
98+ func encodeField (in reflect.Value , meta fieldMeta ) (node * node , err error ) {
7999 in , isNil := deref (in )
80100 if isNil {
81101 return nil , nil
@@ -95,29 +115,34 @@ func encodeField(in reflect.Value, meta fieldMeta) (node *Node, err error) {
95115 return encodePrimitive (in , meta )
96116
97117 case reflect .Struct :
118+ if in .Type ().AssignableTo (reflect .TypeOf (cty.Value {})) {
119+ meta .expression = true
120+ str , _ := ValueToString (in .Interface ().(cty.Value ))
121+ return encodePrimitive (reflect .ValueOf (str ), meta )
122+ }
98123 return encodeStruct (in , meta )
99124 default :
100125 return nil , fmt .Errorf ("cannot encode kind %s to HCL" , in .Kind ())
101126 }
102127}
103128
104- // encodePrimitive converts a primitive value into a Node contains its tokens
105- func encodePrimitive (in reflect.Value , meta fieldMeta ) (* Node , error ) {
129+ // encodePrimitive converts a primitive value into a node contains its tokens
130+ func encodePrimitive (in reflect.Value , meta fieldMeta ) (* node , error ) {
106131 // Keys must be literals, so we don't tokenize.
107132 if meta .key {
108133 k := cty .StringVal (in .String ())
109- return & Node {Value : & k }, nil
134+ return & node {Value : & k }, nil
110135 }
111136 tkn , err := tokenize (in , meta )
112137 if err != nil {
113138 return nil , err
114139 }
115140
116- return & Node {Tokens : tkn }, nil
141+ return & node {Tokens : tkn }, nil
117142}
118143
119144// encodeList converts a slice into either a block list or a primitive list depending on its element type
120- func encodeList (in reflect.Value , meta fieldMeta ) (* Node , error ) {
145+ func encodeList (in reflect.Value , meta fieldMeta ) (* node , error ) {
121146 childType := in .Type ().Elem ()
122147
123148childLoop:
@@ -140,13 +165,13 @@ childLoop:
140165
141166// encodePrimitiveList converts a slice of primitive values to an ast.ListType. An
142167// ast.ObjectKey is never returned.
143- func encodePrimitiveList (in reflect.Value , meta fieldMeta ) (* Node , error ) {
168+ func encodePrimitiveList (in reflect.Value , meta fieldMeta ) (* node , error ) {
144169 return encodePrimitive (in , meta )
145170}
146171
147172// encodeBlockList converts a slice of non-primitive types to an ast.ObjectList. An
148173// ast.ObjectKey is never returned.
149- func encodeBlockList (in reflect.Value , meta fieldMeta ) (* Node , error ) {
174+ func encodeBlockList (in reflect.Value , meta fieldMeta ) (* node , error ) {
150175 var blocks []* hclwrite.Block
151176
152177 if ! meta .repeatBlock {
@@ -164,34 +189,11 @@ func encodeBlockList(in reflect.Value, meta fieldMeta) (*Node, error) {
164189 blocks = append (blocks , node .Block )
165190 }
166191
167- return & Node {BlockList : blocks }, nil
168- }
169-
170- type Node struct {
171- Block * hclwrite.Block
172- BlockList []* hclwrite.Block
173- Value * cty.Value
174- Tokens hclwrite.Tokens
175- }
176-
177- func (n Node ) isValue () bool {
178- return n .Value != nil
179- }
180-
181- func (n Node ) isBlock () bool {
182- return n .Block != nil
183- }
184-
185- func (n Node ) isBlockList () bool {
186- return n .BlockList != nil
187- }
188-
189- func (n Node ) isTokens () bool {
190- return n .Tokens != nil
192+ return & node {BlockList : blocks }, nil
191193}
192194
193195// encodeStruct converts a struct type into a block
194- func encodeStruct (in reflect.Value , parentMeta fieldMeta ) (* Node , error ) {
196+ func encodeStruct (in reflect.Value , parentMeta fieldMeta ) (* node , error ) {
195197 l := in .NumField ()
196198 block := hclwrite .NewBlock (parentMeta .name , nil )
197199
@@ -238,7 +240,7 @@ func encodeStruct(in reflect.Value, parentMeta fieldMeta) (*Node, error) {
238240
239241 if val .isBlock () {
240242 if meta .squash {
241- SquashBlock (val .Block , block .Body ())
243+ squashBlock (val .Block , block .Body ())
242244 for _ , label := range val .Block .Labels () {
243245 block .SetLabels (append (block .Labels (), label ))
244246 }
@@ -260,179 +262,15 @@ func encodeStruct(in reflect.Value, parentMeta fieldMeta) (*Node, error) {
260262
261263 }
262264
263- return & Node {Block : block }, nil
265+ return & node {Block : block }, nil
264266}
265267
266- func SquashBlock (innerBlock * hclwrite.Block , block * hclwrite.Body ) {
268+ func squashBlock (innerBlock * hclwrite.Block , block * hclwrite.Body ) {
267269 tkns := innerBlock .Body ().BuildTokens (nil )
268270 block .AppendUnstructuredTokens (tkns )
269271
270272}
271273
272- func convertTokens (tokens hclsyntax.Tokens ) hclwrite.Tokens {
273- var result []* hclwrite.Token
274- for _ , token := range tokens {
275- result = append (result , & hclwrite.Token {
276- Type : token .Type ,
277- Bytes : token .Bytes ,
278- SpacesBefore : 0 ,
279- })
280- }
281- return result
282- }
283-
284- // tokenize converts a primitive type into tokens. structs and maps are converted into objects and slices are converted
285- // into tuples.
286- func tokenize (in reflect.Value , meta fieldMeta ) (tkns hclwrite.Tokens , err error ) {
287-
288- tokenEqual := hclwrite.Token {
289- Type : hclsyntax .TokenEqual ,
290- Bytes : []byte ("=" ),
291- SpacesBefore : 0 ,
292- }
293- tokenComma := hclwrite.Token {
294- Type : hclsyntax .TokenComma ,
295- Bytes : []byte ("," ),
296- SpacesBefore : 0 ,
297- }
298- tokenOCurlyBrace := hclwrite.Token {
299- Type : hclsyntax .TokenOBrace ,
300- Bytes : []byte ("{" ),
301- SpacesBefore : 0 ,
302- }
303- tokenCCurlyBrace := hclwrite.Token {
304- Type : hclsyntax .TokenCBrace ,
305- Bytes : []byte ("}" ),
306- SpacesBefore : 0 ,
307- }
308-
309- switch in .Kind () {
310- case reflect .Bool :
311- return hclwrite .TokensForValue (cty .BoolVal (in .Bool ())), nil
312-
313- case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 :
314- return hclwrite .TokensForValue (cty .NumberUIntVal (in .Uint ())), nil
315-
316- case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
317- return hclwrite .TokensForValue (cty .NumberIntVal (in .Int ())), nil
318-
319- case reflect .Float64 :
320- return hclwrite .TokensForValue (cty .NumberFloatVal (in .Float ())), nil
321-
322- case reflect .String :
323- val := in .String ()
324- if ! meta .expression {
325- val = fmt .Sprintf (`"%s"` , EscapeString (val ))
326- }
327- // Unfortunately hcl escapes template expressions (${...}) when using hclwrite.TokensForValue. So we escape
328- // everything but template expressions and then parse the expression into tokens.
329- tokens , diags := hclsyntax .LexExpression ([]byte (val ), meta .name , hcl.Pos {
330- Line : 0 ,
331- Column : 0 ,
332- Byte : 0 ,
333- })
334-
335- if diags != nil {
336- return nil , fmt .Errorf ("error when parsing string %s: %v" , val , diags .Error ())
337- }
338- return convertTokens (tokens ), nil
339- case reflect .Pointer , reflect .Interface :
340- val , isNil := deref (in )
341- if isNil {
342- return nil , nil
343- }
344- return tokenize (val , meta )
345- case reflect .Struct :
346- var tokens []* hclwrite.Token
347- tokens = append (tokens , & tokenOCurlyBrace )
348- for i := 0 ; i < in .NumField (); i ++ {
349- field := in .Type ().Field (i )
350- meta := extractFieldMeta (field )
351-
352- rawVal := in .Field (i )
353- if meta .omitEmpty {
354- zeroVal := reflect .Zero (rawVal .Type ()).Interface ()
355- if reflect .DeepEqual (rawVal .Interface (), zeroVal ) {
356- continue
357- }
358- }
359- val , err := tokenize (rawVal , meta )
360- if err != nil {
361- return nil , err
362- }
363- for _ , tkn := range hclwrite .TokensForValue (cty .StringVal (meta .name )) {
364- tokens = append (tokens , tkn )
365- }
366- tokens = append (tokens , & tokenEqual )
367- for _ , tkn := range val {
368- tokens = append (tokens , tkn )
369- }
370- if i < in .NumField ()- 1 {
371- tokens = append (tokens , & tokenComma )
372- }
373- }
374- tokens = append (tokens , & tokenCCurlyBrace )
375- return tokens , nil
376- case reflect .Slice :
377- var tokens []* hclwrite.Token
378- tokens = append (tokens , & hclwrite.Token {
379- Type : hclsyntax .TokenOBrace ,
380- Bytes : []byte ("[" ),
381- SpacesBefore : 0 ,
382- })
383- for i := 0 ; i < in .Len (); i ++ {
384- value , err := tokenize (in .Index (i ), meta )
385- if err != nil {
386- return nil , err
387- }
388- for _ , tkn := range value {
389- tokens = append (tokens , tkn )
390- }
391- if i < in .Len ()- 1 {
392- tokens = append (tokens , & tokenComma )
393- }
394- }
395- tokens = append (tokens , & hclwrite.Token {
396- Type : hclsyntax .TokenCBrace ,
397- Bytes : []byte ("]" ),
398- SpacesBefore : 0 ,
399- })
400- return tokens , nil
401- case reflect .Map :
402- if keyType := in .Type ().Key ().Kind (); keyType != reflect .String {
403- return nil , fmt .Errorf ("map keys must be strings, %s given" , keyType )
404- }
405- var tokens []* hclwrite.Token
406- tokens = append (tokens , & tokenOCurlyBrace )
407-
408- var keys []string
409- for _ , k := range in .MapKeys () {
410- keys = append (keys , k .String ())
411- }
412- sort .Strings (keys )
413- for i , k := range keys {
414- val , err := tokenize (in .MapIndex (reflect .ValueOf (k )), meta )
415- if err != nil {
416- return nil , err
417- }
418- for _ , tkn := range hclwrite .TokensForValue (cty .StringVal (k )) {
419- tokens = append (tokens , tkn )
420- }
421- tokens = append (tokens , & tokenEqual )
422- for _ , tkn := range val {
423- tokens = append (tokens , tkn )
424- }
425- if i < len (keys )- 1 {
426- tokens = append (tokens , & tokenComma )
427- }
428- }
429- tokens = append (tokens , & tokenCCurlyBrace )
430- return tokens , nil
431- }
432-
433- return nil , fmt .Errorf ("cannot encode primitive kind %s to token" , in .Kind ())
434- }
435-
436274// extractFieldMeta pulls information about struct fields and the optional HCL tags
437275func extractFieldMeta (f reflect.StructField ) (meta fieldMeta ) {
438276 if f .Anonymous {
0 commit comments