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