forked from hashicorp/terraform-plugin-framework
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexpression_steps.go
186 lines (144 loc) · 4.38 KB
/
expression_steps.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
package path
import "strings"
// ExpressionSteps represents an ordered collection of attribute path
// expressions.
type ExpressionSteps []ExpressionStep
// Append adds the given ExpressionSteps to the end of the previous ExpressionSteps and
// returns the combined result.
func (s *ExpressionSteps) Append(steps ...ExpressionStep) ExpressionSteps {
if s == nil {
return steps
}
*s = append(*s, steps...)
return *s
}
// Copy returns a duplicate of the steps that is safe to modify without
// affecting the original. Returns nil if the original steps is nil.
func (s ExpressionSteps) Copy() ExpressionSteps {
if s == nil {
return nil
}
copiedExpressionSteps := make(ExpressionSteps, len(s))
copy(copiedExpressionSteps, s)
return copiedExpressionSteps
}
// Equal returns true if the given ExpressionSteps are equivalent.
func (s ExpressionSteps) Equal(o ExpressionSteps) bool {
if len(s) != len(o) {
return false
}
for stepIndex, step := range s {
if !step.Equal(o[stepIndex]) {
return false
}
}
return true
}
// LastStep returns the final ExpressionStep and the remaining ExpressionSteps.
func (s ExpressionSteps) LastStep() (ExpressionStep, ExpressionSteps) {
if len(s) == 0 {
return nil, ExpressionSteps{}
}
if len(s) == 1 {
return s[0], ExpressionSteps{}
}
return s[len(s)-1], s[:len(s)-1]
}
// Matches returns true if the given PathSteps match each ExpressionStep.
//
// Any ExpressionStepParent will automatically be resolved.
func (s ExpressionSteps) Matches(pathSteps PathSteps) bool {
resolvedExpressionSteps := s.Resolve()
// Empty expression should not match anything to prevent false positives.
if len(resolvedExpressionSteps) == 0 {
return false
}
if len(resolvedExpressionSteps) != len(pathSteps) {
return false
}
for stepIndex, expressionStep := range resolvedExpressionSteps {
if !expressionStep.Matches(pathSteps[stepIndex]) {
return false
}
}
return true
}
// MatchesParent returns true if the given PathSteps match each ExpressionStep
// until there are no more PathSteps. This is helpful for determining if the
// PathSteps would potentially match the full ExpressionSteps during
// depth-first traversal.
//
// Any ExpressionStepParent will automatically be resolved.
func (s ExpressionSteps) MatchesParent(pathSteps PathSteps) bool {
resolvedExpressionSteps := s.Resolve()
// Empty expression should not match anything to prevent false positives.
// Ensure to not return false on an empty path since walking a path always
// starts with no steps.
if len(resolvedExpressionSteps) == 0 {
return false
}
// Path steps deeper than or equal to the expression steps should not match
// as a potential parent.
if len(pathSteps) >= len(resolvedExpressionSteps) {
return false
}
for stepIndex, pathStep := range pathSteps {
if !resolvedExpressionSteps[stepIndex].Matches(pathStep) {
return false
}
}
return true
}
// NextStep returns the first ExpressionStep and the remaining ExpressionSteps.
func (s ExpressionSteps) NextStep() (ExpressionStep, ExpressionSteps) {
if len(s) == 0 {
return nil, s
}
return s[0], s[1:]
}
// Resolve returns a copy of ExpressionSteps without any ExpressionStepParent.
//
// Returns empty ExpressionSteps if any ExpressionStepParent attempt to go
// beyond the first element. Returns nil if the original steps is nil.
func (s ExpressionSteps) Resolve() ExpressionSteps {
if s == nil {
return nil
}
result := make(ExpressionSteps, 0, len(s))
// This might not be the most efficient or prettiest algorithm, but it
// works for now.
for _, step := range s {
_, ok := step.(ExpressionStepParent)
if !ok {
result.Append(step)
continue
}
// Allow parent traversal up to the root, but not beyond.
if len(result) == 0 {
return ExpressionSteps{}
}
_, remaining := result.LastStep()
if len(remaining) == 0 {
result = ExpressionSteps{}
continue
}
result = remaining
}
return result
}
// String returns the human-readable representation of the ExpressionSteps.
// It is intended for logging and error messages and is not protected by
// compatibility guarantees.
func (s ExpressionSteps) String() string {
var result strings.Builder
for stepIndex, step := range s {
if stepIndex != 0 {
switch step.(type) {
case ExpressionStepAttributeNameExact, ExpressionStepParent:
result.WriteString(".")
}
}
result.WriteString(step.String())
}
return result.String()
}