github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/configs/configschema/decoder_spec.go (about)

     1  package configschema
     2  
     3  import (
     4  	"github.com/hashicorp/hcl/v2/hcldec"
     5  )
     6  
     7  var mapLabelNames = []string{"key"}
     8  
     9  // DecoderSpec returns a hcldec.Spec that can be used to decode a HCL Body
    10  // using the facilities in the hcldec package.
    11  //
    12  // The returned specification is guaranteed to return a value of the same type
    13  // returned by method ImpliedType, but it may contain null values if any of the
    14  // block attributes are defined as optional and/or computed respectively.
    15  func (b *Block) DecoderSpec() hcldec.Spec {
    16  	ret := hcldec.ObjectSpec{}
    17  	if b == nil {
    18  		return ret
    19  	}
    20  
    21  	for name, attrS := range b.Attributes {
    22  		ret[name] = attrS.decoderSpec(name)
    23  	}
    24  
    25  	for name, blockS := range b.BlockTypes {
    26  		if _, exists := ret[name]; exists {
    27  			// This indicates an invalid schema, since it's not valid to
    28  			// define both an attribute and a block type of the same name.
    29  			// However, we don't raise this here since it's checked by
    30  			// InternalValidate.
    31  			continue
    32  		}
    33  
    34  		childSpec := blockS.Block.DecoderSpec()
    35  
    36  		// We can only validate 0 or 1 for MinItems, because a dynamic block
    37  		// may satisfy any number of min items while only having a single
    38  		// block in the config. We cannot validate MaxItems because a
    39  		// configuration may have any number of dynamic blocks
    40  		minItems := 0
    41  		if blockS.MinItems > 1 {
    42  			minItems = 1
    43  		}
    44  
    45  		switch blockS.Nesting {
    46  		case NestingSingle, NestingGroup:
    47  			ret[name] = &hcldec.BlockSpec{
    48  				TypeName: name,
    49  				Nested:   childSpec,
    50  				Required: blockS.MinItems == 1,
    51  			}
    52  			if blockS.Nesting == NestingGroup {
    53  				ret[name] = &hcldec.DefaultSpec{
    54  					Primary: ret[name],
    55  					Default: &hcldec.LiteralSpec{
    56  						Value: blockS.EmptyValue(),
    57  					},
    58  				}
    59  			}
    60  		case NestingList:
    61  			// We prefer to use a list where possible, since it makes our
    62  			// implied type more complete, but if there are any
    63  			// dynamically-typed attributes inside we must use a tuple
    64  			// instead, at the expense of our type then not being predictable.
    65  			if blockS.Block.ImpliedType().HasDynamicTypes() {
    66  				ret[name] = &hcldec.BlockTupleSpec{
    67  					TypeName: name,
    68  					Nested:   childSpec,
    69  					MinItems: minItems,
    70  				}
    71  			} else {
    72  				ret[name] = &hcldec.BlockListSpec{
    73  					TypeName: name,
    74  					Nested:   childSpec,
    75  					MinItems: minItems,
    76  				}
    77  			}
    78  		case NestingSet:
    79  			// We forbid dynamically-typed attributes inside NestingSet in
    80  			// InternalValidate, so we don't do anything special to handle
    81  			// that here. (There is no set analog to tuple and object types,
    82  			// because cty's set implementation depends on knowing the static
    83  			// type in order to properly compute its internal hashes.)
    84  			ret[name] = &hcldec.BlockSetSpec{
    85  				TypeName: name,
    86  				Nested:   childSpec,
    87  				MinItems: minItems,
    88  			}
    89  		case NestingMap:
    90  			// We prefer to use a list where possible, since it makes our
    91  			// implied type more complete, but if there are any
    92  			// dynamically-typed attributes inside we must use a tuple
    93  			// instead, at the expense of our type then not being predictable.
    94  			if blockS.Block.ImpliedType().HasDynamicTypes() {
    95  				ret[name] = &hcldec.BlockObjectSpec{
    96  					TypeName:   name,
    97  					Nested:     childSpec,
    98  					LabelNames: mapLabelNames,
    99  				}
   100  			} else {
   101  				ret[name] = &hcldec.BlockMapSpec{
   102  					TypeName:   name,
   103  					Nested:     childSpec,
   104  					LabelNames: mapLabelNames,
   105  				}
   106  			}
   107  		default:
   108  			// Invalid nesting type is just ignored. It's checked by
   109  			// InternalValidate.
   110  			continue
   111  		}
   112  	}
   113  
   114  	return ret
   115  }
   116  
   117  func (a *Attribute) decoderSpec(name string) hcldec.Spec {
   118  	return &hcldec.AttrSpec{
   119  		Name:     name,
   120  		Type:     a.Type,
   121  		Required: a.Required,
   122  	}
   123  }