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 }