11"""A utility class for constructing HCL documents from Python code."""
2-
32from typing import List , Optional
43
4+ from collections import defaultdict
5+
6+ from hcl2 .const import START_LINE_KEY , END_LINE_KEY
7+
58
69class Builder :
710 """
@@ -15,49 +18,69 @@ class Builder:
1518 """
1619
1720 def __init__ (self , attributes : Optional [dict ] = None ):
18- self .blocks : dict = {}
21+ self .blocks : dict = defaultdict ( list )
1922 self .attributes = attributes or {}
2023
2124 def block (
22- self , block_type : str , labels : Optional [List [str ]] = None , ** attributes
25+ self ,
26+ block_type : str ,
27+ labels : Optional [List [str ]] = None ,
28+ __nested_builder__ : Optional ["Builder" ] = None ,
29+ ** attributes
2330 ) -> "Builder" :
2431 """Create a block within this HCL document."""
32+
33+ if __nested_builder__ is self :
34+ raise ValueError (
35+ "__nested__builder__ cannot be the same instance as instance this method is called on"
36+ )
37+
2538 labels = labels or []
2639 block = Builder (attributes )
2740
28- # initialize a holder for blocks of that type
29- if block_type not in self .blocks :
30- self .blocks [block_type ] = []
31-
3241 # store the block in the document
33- self .blocks [block_type ].append ((labels .copy (), block ))
42+ self .blocks [block_type ].append ((labels .copy (), block , __nested_builder__ ))
3443
3544 return block
3645
3746 def build (self ):
3847 """Return the Python dictionary for this HCL document."""
39- body = {
40- "__start_line__" : - 1 ,
41- "__end_line__" : - 1 ,
42- ** self .attributes ,
43- }
48+ body = defaultdict (list )
4449
45- for block_type , blocks in self .blocks .items ():
50+ body .update (
51+ {
52+ START_LINE_KEY : - 1 ,
53+ END_LINE_KEY : - 1 ,
54+ ** self .attributes ,
55+ }
56+ )
4657
47- # initialize a holder for blocks of that type
48- if block_type not in body :
49- body [block_type ] = []
58+ for block_type , blocks in self .blocks .items ():
5059
51- for labels , block_builder in blocks :
60+ for labels , block_builder , nested_blocks in blocks :
5261 # build the sub-block
5362 block = block_builder .build ()
5463
64+ if nested_blocks :
65+ self ._add_nested_blocks (block , nested_blocks )
66+
5567 # apply any labels
56- labels .reverse ()
57- for label in labels :
68+ for label in reversed (labels ):
5869 block = {label : block }
5970
6071 # store it in the body
6172 body [block_type ].append (block )
6273
6374 return body
75+
76+ def _add_nested_blocks (
77+ self , block : dict , nested_blocks_builder : "Builder"
78+ ) -> "dict" :
79+ """Add nested blocks defined within another `Builder` instance to the `block` dictionary"""
80+ nested_block = nested_blocks_builder .build ()
81+ for key , value in nested_block .items ():
82+ if key not in (START_LINE_KEY , END_LINE_KEY ):
83+ if key not in block .keys ():
84+ block [key ] = []
85+ block [key ].extend (value )
86+ return block
0 commit comments