github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/jobspec2/hclutil/blockattrs.go (about) 1 package hclutil 2 3 import ( 4 "github.com/hashicorp/hcl/v2" 5 hcls "github.com/hashicorp/hcl/v2/hclsyntax" 6 ) 7 8 // BlocksAsAttrs rewrites the hcl.Body so that hcl blocks are treated as 9 // attributes when schema is unknown. 10 // 11 // This conversion is necessary for parsing task driver configs, as they can be 12 // arbitrary nested without pre-defined schema. 13 // 14 // More concretely, it changes the following: 15 // 16 // ``` 17 // 18 // config { 19 // meta { ... } 20 // } 21 // 22 // ``` 23 // 24 // to 25 // 26 // ``` 27 // 28 // config { 29 // meta = { ... } # <- attribute now 30 // } 31 // 32 // ``` 33 func BlocksAsAttrs(body hcl.Body) hcl.Body { 34 if hclb, ok := body.(*hcls.Body); ok { 35 return &blockAttrs{body: hclb} 36 } 37 return body 38 } 39 40 type blockAttrs struct { 41 body hcl.Body 42 43 hiddenAttrs map[string]struct{} 44 hiddenBlocks map[string]struct{} 45 } 46 47 func (b *blockAttrs) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) { 48 bc, diags := b.body.Content(schema) 49 bc.Blocks = expandBlocks(bc.Blocks) 50 return bc, diags 51 } 52 func (b *blockAttrs) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) { 53 bc, remainBody, diags := b.body.PartialContent(schema) 54 bc.Blocks = expandBlocks(bc.Blocks) 55 56 remain := &blockAttrs{ 57 body: remainBody, 58 hiddenAttrs: map[string]struct{}{}, 59 hiddenBlocks: map[string]struct{}{}, 60 } 61 for name := range b.hiddenAttrs { 62 remain.hiddenAttrs[name] = struct{}{} 63 } 64 for typeName := range b.hiddenBlocks { 65 remain.hiddenBlocks[typeName] = struct{}{} 66 } 67 for _, attrS := range schema.Attributes { 68 remain.hiddenAttrs[attrS.Name] = struct{}{} 69 } 70 for _, blockS := range schema.Blocks { 71 remain.hiddenBlocks[blockS.Type] = struct{}{} 72 } 73 74 return bc, remain, diags 75 } 76 77 func (b *blockAttrs) JustAttributes() (hcl.Attributes, hcl.Diagnostics) { 78 body, ok := b.body.(*hcls.Body) 79 if !ok { 80 return b.body.JustAttributes() 81 } 82 83 attrs := make(hcl.Attributes) 84 var diags hcl.Diagnostics 85 86 if body.Attributes == nil && len(body.Blocks) == 0 { 87 return attrs, diags 88 } 89 90 for name, attr := range body.Attributes { 91 if _, hidden := b.hiddenAttrs[name]; hidden { 92 continue 93 } 94 95 na := attr.AsHCLAttribute() 96 na.Expr = attrExpr(attr.Expr) 97 attrs[name] = na 98 } 99 100 for _, blocks := range blocksByType(body.Blocks) { 101 if _, hidden := b.hiddenBlocks[blocks[0].Type]; hidden { 102 continue 103 } 104 105 b := blocks[0] 106 attr := &hcls.Attribute{ 107 Name: b.Type, 108 NameRange: b.TypeRange, 109 EqualsRange: b.OpenBraceRange, 110 SrcRange: b.Body.SrcRange, 111 Expr: blocksToExpr(blocks), 112 } 113 114 attrs[blocks[0].Type] = attr.AsHCLAttribute() 115 } 116 117 return attrs, diags 118 } 119 120 func (b *blockAttrs) MissingItemRange() hcl.Range { 121 return b.body.MissingItemRange() 122 } 123 124 func expandBlocks(blocks hcl.Blocks) hcl.Blocks { 125 if len(blocks) == 0 { 126 return blocks 127 } 128 129 r := make([]*hcl.Block, len(blocks)) 130 for i, b := range blocks { 131 nb := *b 132 nb.Body = BlocksAsAttrs(b.Body) 133 r[i] = &nb 134 } 135 return r 136 } 137 138 func blocksByType(blocks hcls.Blocks) map[string]hcls.Blocks { 139 r := map[string]hcls.Blocks{} 140 for _, b := range blocks { 141 r[b.Type] = append(r[b.Type], b) 142 } 143 return r 144 } 145 146 func blocksToExpr(blocks hcls.Blocks) hcls.Expression { 147 if len(blocks) == 0 { 148 panic("unexpected empty blocks") 149 } 150 151 exprs := make([]hcls.Expression, len(blocks)) 152 for i, b := range blocks { 153 exprs[i] = blockToExpr(b) 154 } 155 156 last := blocks[len(blocks)-1] 157 return &hcls.TupleConsExpr{ 158 Exprs: exprs, 159 160 SrcRange: hcl.RangeBetween(blocks[0].OpenBraceRange, last.CloseBraceRange), 161 OpenRange: blocks[0].OpenBraceRange, 162 } 163 } 164 165 func blockToExpr(b *hcls.Block) hcls.Expression { 166 items := []hcls.ObjectConsItem{} 167 168 for _, attr := range b.Body.Attributes { 169 keyExpr := &hcls.ScopeTraversalExpr{ 170 Traversal: hcl.Traversal{ 171 hcl.TraverseRoot{ 172 Name: attr.Name, 173 SrcRange: attr.NameRange, 174 }, 175 }, 176 SrcRange: attr.NameRange, 177 } 178 key := &hcls.ObjectConsKeyExpr{ 179 Wrapped: keyExpr, 180 } 181 182 items = append(items, hcls.ObjectConsItem{ 183 KeyExpr: key, 184 ValueExpr: attrExpr(attr.Expr), 185 }) 186 } 187 188 for _, blocks := range blocksByType(b.Body.Blocks) { 189 keyExpr := &hcls.ScopeTraversalExpr{ 190 Traversal: hcl.Traversal{ 191 hcl.TraverseRoot{ 192 Name: blocks[0].Type, 193 SrcRange: blocks[0].TypeRange, 194 }, 195 }, 196 SrcRange: blocks[0].TypeRange, 197 } 198 key := &hcls.ObjectConsKeyExpr{ 199 Wrapped: keyExpr, 200 } 201 item := hcls.ObjectConsItem{ 202 KeyExpr: key, 203 ValueExpr: blocksToExpr(blocks), 204 } 205 206 items = append(items, item) 207 } 208 209 v := &hcls.ObjectConsExpr{ 210 Items: items, 211 } 212 213 // Create nested maps, with the labels as keys. 214 // Starts wrapping from most inner label to outer 215 for i := len(b.Labels) - 1; i >= 0; i-- { 216 keyExpr := &hcls.ScopeTraversalExpr{ 217 Traversal: hcl.Traversal{ 218 hcl.TraverseRoot{ 219 Name: b.Labels[i], 220 SrcRange: b.LabelRanges[i], 221 }, 222 }, 223 SrcRange: b.LabelRanges[i], 224 } 225 key := &hcls.ObjectConsKeyExpr{ 226 Wrapped: keyExpr, 227 } 228 item := hcls.ObjectConsItem{ 229 KeyExpr: key, 230 ValueExpr: &hcls.TupleConsExpr{ 231 Exprs: []hcls.Expression{v}, 232 }, 233 } 234 235 v = &hcls.ObjectConsExpr{ 236 Items: []hcls.ObjectConsItem{item}, 237 } 238 239 } 240 return v 241 } 242 243 func attrExpr(expr hcls.Expression) hcls.Expression { 244 if _, ok := expr.(*hcls.ObjectConsExpr); ok { 245 return &hcls.TupleConsExpr{ 246 Exprs: []hcls.Expression{expr}, 247 SrcRange: expr.Range(), 248 OpenRange: expr.StartRange(), 249 } 250 } 251 252 return expr 253 }