github.com/hernad/nomad@v1.6.112/jobspec2/hclutil/blockattrs.go (about)

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