Skip to content

Commit 89baaa2

Browse files
authored
tfsdk: Ensure Config, Plan, and State PathMatches will return null and unknown parent paths (#410)
This will also now return an error diagnostic if a given path expression is invalid for the schema data. The `(path.Expression).MergeExpressions(...path.Expression) Expressions` is a helper method for the common use case of combining an attribute path expression with an incoming collection of expressions to perform generic validation across attributes.
1 parent 656b447 commit 89baaa2

File tree

12 files changed

+1774
-159
lines changed

12 files changed

+1774
-159
lines changed

path/expression.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,15 @@ func (e Expression) Matches(path Path) bool {
131131
return e.steps.Matches(path.Steps())
132132
}
133133

134+
// MatchesParent returns true if the given Path is a valid parent for the
135+
// Expression. This is helpful for determining if a child Path would
136+
// potentially match the full Expression during depth-first traversal. Any
137+
// relative expression steps, such as ExpressionStepParent, are automatically
138+
// resolved before matching.
139+
func (e Expression) MatchesParent(path Path) bool {
140+
return e.steps.MatchesParent(path.Steps())
141+
}
142+
134143
// Merge returns a copied expression either with the steps of the given
135144
// expression added to the end of the existing steps, or overwriting the
136145
// steps if the given expression was a root expression.
@@ -152,6 +161,24 @@ func (e Expression) Merge(other Expression) Expression {
152161
return copiedExpression
153162
}
154163

164+
// MergeExpressions returns collection of expressions that calls Merge() on
165+
// the current expression with each of the others.
166+
func (e Expression) MergeExpressions(others ...Expression) Expressions {
167+
var result Expressions
168+
169+
if len(others) == 0 {
170+
result.Append(e)
171+
172+
return result
173+
}
174+
175+
for _, other := range others {
176+
result.Append(e.Merge(other))
177+
}
178+
179+
return result
180+
}
181+
155182
// Resolve returns a copied expression with any relative steps, such as
156183
// ExpressionStepParent, resolved. This is not necessary before calling methods
157184
// such as Matches(), however it can be useful before returning the String()

path/expression_steps.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,37 @@ func (s ExpressionSteps) Matches(pathSteps PathSteps) bool {
8484
return true
8585
}
8686

87+
// MatchesParent returns true if the given PathSteps match each ExpressionStep
88+
// until there are no more PathSteps. This is helpful for determining if the
89+
// PathSteps would potentially match the full ExpressionSteps during
90+
// depth-first traversal.
91+
//
92+
// Any ExpressionStepParent will automatically be resolved.
93+
func (s ExpressionSteps) MatchesParent(pathSteps PathSteps) bool {
94+
resolvedExpressionSteps := s.Resolve()
95+
96+
// Empty expression should not match anything to prevent false positives.
97+
// Ensure to not return false on an empty path since walking a path always
98+
// starts with no steps.
99+
if len(resolvedExpressionSteps) == 0 {
100+
return false
101+
}
102+
103+
// Path steps deeper than or equal to the expression steps should not match
104+
// as a potential parent.
105+
if len(pathSteps) >= len(resolvedExpressionSteps) {
106+
return false
107+
}
108+
109+
for stepIndex, pathStep := range pathSteps {
110+
if !resolvedExpressionSteps[stepIndex].Matches(pathStep) {
111+
return false
112+
}
113+
}
114+
115+
return true
116+
}
117+
87118
// NextStep returns the first ExpressionStep and the remaining ExpressionSteps.
88119
func (s ExpressionSteps) NextStep() (ExpressionStep, ExpressionSteps) {
89120
if len(s) == 0 {

0 commit comments

Comments
 (0)