kubeform.dev/terraform-backend-sdk@v0.0.0-20220310143633-45f07fe731c5/configs/synth_body.go (about)

     1  package configs
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/hcl/v2"
     7  	"github.com/hashicorp/hcl/v2/hclsyntax"
     8  	"github.com/zclconf/go-cty/cty"
     9  )
    10  
    11  // SynthBody produces a synthetic hcl.Body that behaves as if it had attributes
    12  // corresponding to the elements given in the values map.
    13  //
    14  // This is useful in situations where, for example, values provided on the
    15  // command line can override values given in configuration, using MergeBodies.
    16  //
    17  // The given filename is used in case any diagnostics are returned. Since
    18  // the created body is synthetic, it is likely that this will not be a "real"
    19  // filename. For example, if from a command line argument it could be
    20  // a representation of that argument's name, such as "-var=...".
    21  func SynthBody(filename string, values map[string]cty.Value) hcl.Body {
    22  	return synthBody{
    23  		Filename: filename,
    24  		Values:   values,
    25  	}
    26  }
    27  
    28  type synthBody struct {
    29  	Filename string
    30  	Values   map[string]cty.Value
    31  }
    32  
    33  func (b synthBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
    34  	content, remain, diags := b.PartialContent(schema)
    35  	remainS := remain.(synthBody)
    36  	for name := range remainS.Values {
    37  		diags = append(diags, &hcl.Diagnostic{
    38  			Severity: hcl.DiagError,
    39  			Summary:  "Unsupported attribute",
    40  			Detail:   fmt.Sprintf("An attribute named %q is not expected here.", name),
    41  			Subject:  b.synthRange().Ptr(),
    42  		})
    43  	}
    44  	return content, diags
    45  }
    46  
    47  func (b synthBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
    48  	var diags hcl.Diagnostics
    49  	content := &hcl.BodyContent{
    50  		Attributes:       make(hcl.Attributes),
    51  		MissingItemRange: b.synthRange(),
    52  	}
    53  
    54  	remainValues := make(map[string]cty.Value)
    55  	for attrName, val := range b.Values {
    56  		remainValues[attrName] = val
    57  	}
    58  
    59  	for _, attrS := range schema.Attributes {
    60  		delete(remainValues, attrS.Name)
    61  		val, defined := b.Values[attrS.Name]
    62  		if !defined {
    63  			if attrS.Required {
    64  				diags = append(diags, &hcl.Diagnostic{
    65  					Severity: hcl.DiagError,
    66  					Summary:  "Missing required attribute",
    67  					Detail:   fmt.Sprintf("The attribute %q is required, but no definition was found.", attrS.Name),
    68  					Subject:  b.synthRange().Ptr(),
    69  				})
    70  			}
    71  			continue
    72  		}
    73  		content.Attributes[attrS.Name] = b.synthAttribute(attrS.Name, val)
    74  	}
    75  
    76  	// We just ignore blocks altogether, because this body type never has
    77  	// nested blocks.
    78  
    79  	remain := synthBody{
    80  		Filename: b.Filename,
    81  		Values:   remainValues,
    82  	}
    83  
    84  	return content, remain, diags
    85  }
    86  
    87  func (b synthBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
    88  	ret := make(hcl.Attributes)
    89  	for name, val := range b.Values {
    90  		ret[name] = b.synthAttribute(name, val)
    91  	}
    92  	return ret, nil
    93  }
    94  
    95  func (b synthBody) MissingItemRange() hcl.Range {
    96  	return b.synthRange()
    97  }
    98  
    99  func (b synthBody) synthAttribute(name string, val cty.Value) *hcl.Attribute {
   100  	rng := b.synthRange()
   101  	return &hcl.Attribute{
   102  		Name: name,
   103  		Expr: &hclsyntax.LiteralValueExpr{
   104  			Val:      val,
   105  			SrcRange: rng,
   106  		},
   107  		NameRange: rng,
   108  		Range:     rng,
   109  	}
   110  }
   111  
   112  func (b synthBody) synthRange() hcl.Range {
   113  	return hcl.Range{
   114  		Filename: b.Filename,
   115  		Start:    hcl.Pos{Line: 1, Column: 1},
   116  		End:      hcl.Pos{Line: 1, Column: 1},
   117  	}
   118  }