]>
Commit | Line | Data |
---|---|---|
15c0b25d AP |
1 | package cty |
2 | ||
3 | import ( | |
4 | "errors" | |
5 | "fmt" | |
6 | ) | |
7 | ||
8 | // A Path is a sequence of operations to locate a nested value within a | |
9 | // data structure. | |
10 | // | |
11 | // The empty Path represents the given item. Any PathSteps within represent | |
12 | // taking a single step down into a data structure. | |
13 | // | |
14 | // Path has some convenience methods for gradually constructing a path, | |
15 | // but callers can also feel free to just produce a slice of PathStep manually | |
16 | // and convert to this type, which may be more appropriate in environments | |
17 | // where memory pressure is a concern. | |
107c1cdb ND |
18 | // |
19 | // Although a Path is technically mutable, by convention callers should not | |
20 | // mutate a path once it has been built and passed to some other subsystem. | |
21 | // Instead, use Copy and then mutate the copy before using it. | |
15c0b25d AP |
22 | type Path []PathStep |
23 | ||
24 | // PathStep represents a single step down into a data structure, as part | |
25 | // of a Path. PathStep is a closed interface, meaning that the only | |
26 | // permitted implementations are those within this package. | |
27 | type PathStep interface { | |
28 | pathStepSigil() pathStepImpl | |
29 | Apply(Value) (Value, error) | |
30 | } | |
31 | ||
32 | // embed pathImpl into a struct to declare it a PathStep implementation | |
33 | type pathStepImpl struct{} | |
34 | ||
35 | func (p pathStepImpl) pathStepSigil() pathStepImpl { | |
36 | return p | |
37 | } | |
38 | ||
39 | // Index returns a new Path that is the reciever with an IndexStep appended | |
40 | // to the end. | |
41 | // | |
42 | // This is provided as a convenient way to construct paths, but each call | |
43 | // will create garbage so it should not be used where memory pressure is a | |
44 | // concern. | |
45 | func (p Path) Index(v Value) Path { | |
46 | ret := make(Path, len(p)+1) | |
47 | copy(ret, p) | |
48 | ret[len(p)] = IndexStep{ | |
49 | Key: v, | |
50 | } | |
51 | return ret | |
52 | } | |
53 | ||
107c1cdb ND |
54 | // IndexPath is a convenience method to start a new Path with an IndexStep. |
55 | func IndexPath(v Value) Path { | |
56 | return Path{}.Index(v) | |
57 | } | |
58 | ||
15c0b25d AP |
59 | // GetAttr returns a new Path that is the reciever with a GetAttrStep appended |
60 | // to the end. | |
61 | // | |
62 | // This is provided as a convenient way to construct paths, but each call | |
63 | // will create garbage so it should not be used where memory pressure is a | |
64 | // concern. | |
65 | func (p Path) GetAttr(name string) Path { | |
66 | ret := make(Path, len(p)+1) | |
67 | copy(ret, p) | |
68 | ret[len(p)] = GetAttrStep{ | |
69 | Name: name, | |
70 | } | |
71 | return ret | |
72 | } | |
73 | ||
107c1cdb ND |
74 | // GetAttrPath is a convenience method to start a new Path with a GetAttrStep. |
75 | func GetAttrPath(name string) Path { | |
76 | return Path{}.GetAttr(name) | |
77 | } | |
78 | ||
15c0b25d AP |
79 | // Apply applies each of the steps in turn to successive values starting with |
80 | // the given value, and returns the result. If any step returns an error, | |
81 | // the whole operation returns an error. | |
82 | func (p Path) Apply(val Value) (Value, error) { | |
83 | var err error | |
84 | for i, step := range p { | |
85 | val, err = step.Apply(val) | |
86 | if err != nil { | |
87 | return NilVal, fmt.Errorf("at step %d: %s", i, err) | |
88 | } | |
89 | } | |
90 | return val, nil | |
91 | } | |
92 | ||
93 | // LastStep applies the given path up to the last step and then returns | |
94 | // the resulting value and the final step. | |
95 | // | |
96 | // This is useful when dealing with assignment operations, since in that | |
97 | // case the *value* of the last step is not important (and may not, in fact, | |
98 | // present at all) and we care only about its location. | |
99 | // | |
100 | // Since LastStep applies all steps except the last, it will return errors | |
101 | // for those steps in the same way as Apply does. | |
102 | // | |
103 | // If the path has *no* steps then the returned PathStep will be nil, | |
104 | // representing that any operation should be applied directly to the | |
105 | // given value. | |
106 | func (p Path) LastStep(val Value) (Value, PathStep, error) { | |
107 | var err error | |
108 | ||
109 | if len(p) == 0 { | |
110 | return val, nil, nil | |
111 | } | |
112 | ||
113 | journey := p[:len(p)-1] | |
114 | val, err = journey.Apply(val) | |
115 | if err != nil { | |
116 | return NilVal, nil, err | |
117 | } | |
118 | return val, p[len(p)-1], nil | |
119 | } | |
120 | ||
121 | // Copy makes a shallow copy of the receiver. Often when paths are passed to | |
122 | // caller code they come with the constraint that they are valid only until | |
123 | // the caller returns, due to how they are constructed internally. Callers | |
124 | // can use Copy to conveniently produce a copy of the value that _they_ control | |
125 | // the validity of. | |
126 | func (p Path) Copy() Path { | |
127 | ret := make(Path, len(p)) | |
128 | copy(ret, p) | |
129 | return ret | |
130 | } | |
131 | ||
132 | // IndexStep is a Step implementation representing applying the index operation | |
133 | // to a value, which must be of either a list, map, or set type. | |
134 | // | |
135 | // When describing a path through a *type* rather than a concrete value, | |
136 | // the Key may be an unknown value, indicating that the step applies to | |
137 | // *any* key of the given type. | |
138 | // | |
139 | // When indexing into a set, the Key is actually the element being accessed | |
140 | // itself, since in sets elements are their own identity. | |
141 | type IndexStep struct { | |
142 | pathStepImpl | |
143 | Key Value | |
144 | } | |
145 | ||
146 | // Apply returns the value resulting from indexing the given value with | |
147 | // our key value. | |
148 | func (s IndexStep) Apply(val Value) (Value, error) { | |
107c1cdb ND |
149 | if val == NilVal || val.IsNull() { |
150 | return NilVal, errors.New("cannot index a null value") | |
151 | } | |
152 | ||
15c0b25d AP |
153 | switch s.Key.Type() { |
154 | case Number: | |
107c1cdb | 155 | if !(val.Type().IsListType() || val.Type().IsTupleType()) { |
15c0b25d AP |
156 | return NilVal, errors.New("not a list type") |
157 | } | |
158 | case String: | |
159 | if !val.Type().IsMapType() { | |
160 | return NilVal, errors.New("not a map type") | |
161 | } | |
162 | default: | |
163 | return NilVal, errors.New("key value not number or string") | |
164 | } | |
165 | ||
166 | has := val.HasIndex(s.Key) | |
167 | if !has.IsKnown() { | |
168 | return UnknownVal(val.Type().ElementType()), nil | |
169 | } | |
170 | if !has.True() { | |
171 | return NilVal, errors.New("value does not have given index key") | |
172 | } | |
173 | ||
174 | return val.Index(s.Key), nil | |
175 | } | |
176 | ||
177 | func (s IndexStep) GoString() string { | |
178 | return fmt.Sprintf("cty.IndexStep{Key:%#v}", s.Key) | |
179 | } | |
180 | ||
181 | // GetAttrStep is a Step implementation representing retrieving an attribute | |
182 | // from a value, which must be of an object type. | |
183 | type GetAttrStep struct { | |
184 | pathStepImpl | |
185 | Name string | |
186 | } | |
187 | ||
188 | // Apply returns the value of our named attribute from the given value, which | |
189 | // must be of an object type that has a value of that name. | |
190 | func (s GetAttrStep) Apply(val Value) (Value, error) { | |
107c1cdb ND |
191 | if val == NilVal || val.IsNull() { |
192 | return NilVal, errors.New("cannot access attributes on a null value") | |
193 | } | |
194 | ||
15c0b25d AP |
195 | if !val.Type().IsObjectType() { |
196 | return NilVal, errors.New("not an object type") | |
197 | } | |
198 | ||
199 | if !val.Type().HasAttribute(s.Name) { | |
200 | return NilVal, fmt.Errorf("object has no attribute %q", s.Name) | |
201 | } | |
202 | ||
203 | return val.GetAttr(s.Name), nil | |
204 | } | |
205 | ||
206 | func (s GetAttrStep) GoString() string { | |
207 | return fmt.Sprintf("cty.GetAttrStep{Name:%q}", s.Name) | |
208 | } |