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