8 // A Path is a sequence of operations to locate a nested value within a
11 // The empty Path represents the given item. Any PathSteps within represent
12 // taking a single step down into a data structure.
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.
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.
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)
32 // embed pathImpl into a struct to declare it a PathStep implementation
33 type pathStepImpl struct{}
35 func (p pathStepImpl) pathStepSigil() pathStepImpl {
39 // Index returns a new Path that is the reciever with an IndexStep appended
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
45 func (p Path) Index(v Value) Path {
46 ret := make(Path, len(p)+1)
48 ret[len(p)] = IndexStep{
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)
59 // GetAttr returns a new Path that is the reciever with a GetAttrStep appended
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
65 func (p Path) GetAttr(name string) Path {
66 ret := make(Path, len(p)+1)
68 ret[len(p)] = GetAttrStep{
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)
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) {
84 for i, step := range p {
85 val, err = step.Apply(val)
87 return NilVal, fmt.Errorf("at step %d: %s", i, err)
93 // LastStep applies the given path up to the last step and then returns
94 // the resulting value and the final step.
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.
100 // Since LastStep applies all steps except the last, it will return errors
101 // for those steps in the same way as Apply does.
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
106 func (p Path) LastStep(val Value) (Value, PathStep, error) {
113 journey := p[:len(p)-1]
114 val, err = journey.Apply(val)
116 return NilVal, nil, err
118 return val, p[len(p)-1], nil
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
126 func (p Path) Copy() Path {
127 ret := make(Path, len(p))
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.
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.
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 {
146 // Apply returns the value resulting from indexing the given value with
148 func (s IndexStep) Apply(val Value) (Value, error) {
149 if val == NilVal || val.IsNull() {
150 return NilVal, errors.New("cannot index a null value")
153 switch s.Key.Type() {
155 if !(val.Type().IsListType() || val.Type().IsTupleType()) {
156 return NilVal, errors.New("not a list type")
159 if !val.Type().IsMapType() {
160 return NilVal, errors.New("not a map type")
163 return NilVal, errors.New("key value not number or string")
166 has := val.HasIndex(s.Key)
168 return UnknownVal(val.Type().ElementType()), nil
171 return NilVal, errors.New("value does not have given index key")
174 return val.Index(s.Key), nil
177 func (s IndexStep) GoString() string {
178 return fmt.Sprintf("cty.IndexStep{Key:%#v}", s.Key)
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 {
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) {
191 if val == NilVal || val.IsNull() {
192 return NilVal, errors.New("cannot access attributes on a null value")
195 if !val.Type().IsObjectType() {
196 return NilVal, errors.New("not an object type")
199 if !val.Type().HasAttribute(s.Name) {
200 return NilVal, fmt.Errorf("object has no attribute %q", s.Name)
203 return val.GetAttr(s.Name), nil
206 func (s GetAttrStep) GoString() string {
207 return fmt.Sprintf("cty.GetAttrStep{Name:%q}", s.Name)