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  }