github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/addrs/instance_key.go (about) 1 package addrs 2 3 import ( 4 "fmt" 5 6 "github.com/zclconf/go-cty/cty" 7 "github.com/zclconf/go-cty/cty/gocty" 8 ) 9 10 // InstanceKey represents the key of an instance within an object that 11 // contains multiple instances due to using "count" or "for_each" arguments 12 // in configuration. 13 // 14 // IntKey and StringKey are the two implementations of this type. No other 15 // implementations are allowed. The single instance of an object that _isn't_ 16 // using "count" or "for_each" is represented by NoKey, which is a nil 17 // InstanceKey. 18 type InstanceKey interface { 19 instanceKeySigil() 20 String() string 21 22 // Value returns the cty.Value of the appropriate type for the InstanceKey 23 // value. 24 Value() cty.Value 25 } 26 27 // ParseInstanceKey returns the instance key corresponding to the given value, 28 // which must be known and non-null. 29 // 30 // If an unknown or null value is provided then this function will panic. This 31 // function is intended to deal with the values that would naturally be found 32 // in a hcl.TraverseIndex, which (when parsed from source, at least) can never 33 // contain unknown or null values. 34 func ParseInstanceKey(key cty.Value) (InstanceKey, error) { 35 switch key.Type() { 36 case cty.String: 37 return StringKey(key.AsString()), nil 38 case cty.Number: 39 var idx int 40 err := gocty.FromCtyValue(key, &idx) 41 return IntKey(idx), err 42 default: 43 return NoKey, fmt.Errorf("either a string or an integer is required") 44 } 45 } 46 47 // NoKey represents the absense of an InstanceKey, for the single instance 48 // of a configuration object that does not use "count" or "for_each" at all. 49 var NoKey InstanceKey 50 51 // IntKey is the InstanceKey representation representing integer indices, as 52 // used when the "count" argument is specified or if for_each is used with 53 // a sequence type. 54 type IntKey int 55 56 func (k IntKey) instanceKeySigil() { 57 } 58 59 func (k IntKey) String() string { 60 return fmt.Sprintf("[%d]", int(k)) 61 } 62 63 func (k IntKey) Value() cty.Value { 64 return cty.NumberIntVal(int64(k)) 65 } 66 67 // StringKey is the InstanceKey representation representing string indices, as 68 // used when the "for_each" argument is specified with a map or object type. 69 type StringKey string 70 71 func (k StringKey) instanceKeySigil() { 72 } 73 74 func (k StringKey) String() string { 75 // FIXME: This isn't _quite_ right because Go's quoted string syntax is 76 // slightly different than HCL's, but we'll accept it for now. 77 return fmt.Sprintf("[%q]", string(k)) 78 } 79 80 func (k StringKey) Value() cty.Value { 81 return cty.StringVal(string(k)) 82 } 83 84 // InstanceKeyLess returns true if the first given instance key i should sort 85 // before the second key j, and false otherwise. 86 func InstanceKeyLess(i, j InstanceKey) bool { 87 iTy := instanceKeyType(i) 88 jTy := instanceKeyType(j) 89 90 switch { 91 case i == j: 92 return false 93 case i == NoKey: 94 return true 95 case j == NoKey: 96 return false 97 case iTy != jTy: 98 // The ordering here is arbitrary except that we want NoKeyType 99 // to sort before the others, so we'll just use the enum values 100 // of InstanceKeyType here (where NoKey is zero, sorting before 101 // any other). 102 return uint32(iTy) < uint32(jTy) 103 case iTy == IntKeyType: 104 return int(i.(IntKey)) < int(j.(IntKey)) 105 case iTy == StringKeyType: 106 return string(i.(StringKey)) < string(j.(StringKey)) 107 default: 108 // Shouldn't be possible to get down here in practice, since the 109 // above is exhaustive. 110 return false 111 } 112 } 113 114 func instanceKeyType(k InstanceKey) InstanceKeyType { 115 if _, ok := k.(StringKey); ok { 116 return StringKeyType 117 } 118 if _, ok := k.(IntKey); ok { 119 return IntKeyType 120 } 121 return NoKeyType 122 } 123 124 // InstanceKeyType represents the different types of instance key that are 125 // supported. Usually it is sufficient to simply type-assert an InstanceKey 126 // value to either IntKey or StringKey, but this type and its values can be 127 // used to represent the types themselves, rather than specific values 128 // of those types. 129 type InstanceKeyType rune 130 131 const ( 132 NoKeyType InstanceKeyType = 0 133 IntKeyType InstanceKeyType = 'I' 134 StringKeyType InstanceKeyType = 'S' 135 )