mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-09-18 17:19:58 +00:00
125 lines
4.5 KiB
Go
125 lines
4.5 KiB
Go
|
package hcl
|
||
|
|
||
|
// AbsTraversalForExpr attempts to interpret the given expression as
|
||
|
// an absolute traversal, or returns error diagnostic(s) if that is
|
||
|
// not possible for the given expression.
|
||
|
//
|
||
|
// A particular Expression implementation can support this function by
|
||
|
// offering a method called AsTraversal that takes no arguments and
|
||
|
// returns either a valid absolute traversal or nil to indicate that
|
||
|
// no traversal is possible. Alternatively, an implementation can support
|
||
|
// UnwrapExpression to delegate handling of this function to a wrapped
|
||
|
// Expression object.
|
||
|
//
|
||
|
// In most cases the calling application is interested in the value
|
||
|
// that results from an expression, but in rarer cases the application
|
||
|
// needs to see the the name of the variable and subsequent
|
||
|
// attributes/indexes itself, for example to allow users to give references
|
||
|
// to the variables themselves rather than to their values. An implementer
|
||
|
// of this function should at least support attribute and index steps.
|
||
|
func AbsTraversalForExpr(expr Expression) (Traversal, Diagnostics) {
|
||
|
type asTraversal interface {
|
||
|
AsTraversal() Traversal
|
||
|
}
|
||
|
|
||
|
physExpr := UnwrapExpressionUntil(expr, func(expr Expression) bool {
|
||
|
_, supported := expr.(asTraversal)
|
||
|
return supported
|
||
|
})
|
||
|
|
||
|
if asT, supported := physExpr.(asTraversal); supported {
|
||
|
if traversal := asT.AsTraversal(); traversal != nil {
|
||
|
return traversal, nil
|
||
|
}
|
||
|
}
|
||
|
return nil, Diagnostics{
|
||
|
&Diagnostic{
|
||
|
Severity: DiagError,
|
||
|
Summary: "Invalid expression",
|
||
|
Detail: "A static variable reference is required.",
|
||
|
Subject: expr.Range().Ptr(),
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// RelTraversalForExpr is similar to AbsTraversalForExpr but it returns
|
||
|
// a relative traversal instead. Due to the nature of HCL expressions, the
|
||
|
// first element of the returned traversal is always a TraverseAttr, and
|
||
|
// then it will be followed by zero or more other expressions.
|
||
|
//
|
||
|
// Any expression accepted by AbsTraversalForExpr is also accepted by
|
||
|
// RelTraversalForExpr.
|
||
|
func RelTraversalForExpr(expr Expression) (Traversal, Diagnostics) {
|
||
|
traversal, diags := AbsTraversalForExpr(expr)
|
||
|
if len(traversal) > 0 {
|
||
|
ret := make(Traversal, len(traversal))
|
||
|
copy(ret, traversal)
|
||
|
root := traversal[0].(TraverseRoot)
|
||
|
ret[0] = TraverseAttr{
|
||
|
Name: root.Name,
|
||
|
SrcRange: root.SrcRange,
|
||
|
}
|
||
|
return ret, diags
|
||
|
}
|
||
|
return traversal, diags
|
||
|
}
|
||
|
|
||
|
// ExprAsKeyword attempts to interpret the given expression as a static keyword,
|
||
|
// returning the keyword string if possible, and the empty string if not.
|
||
|
//
|
||
|
// A static keyword, for the sake of this function, is a single identifier.
|
||
|
// For example, the following attribute has an expression that would produce
|
||
|
// the keyword "foo":
|
||
|
//
|
||
|
// example = foo
|
||
|
//
|
||
|
// This function is a variant of AbsTraversalForExpr, which uses the same
|
||
|
// interface on the given expression. This helper constrains the result
|
||
|
// further by requiring only a single root identifier.
|
||
|
//
|
||
|
// This function is intended to be used with the following idiom, to recognize
|
||
|
// situations where one of a fixed set of keywords is required and arbitrary
|
||
|
// expressions are not allowed:
|
||
|
//
|
||
|
// switch hcl.ExprAsKeyword(expr) {
|
||
|
// case "allow":
|
||
|
// // (take suitable action for keyword "allow")
|
||
|
// case "deny":
|
||
|
// // (take suitable action for keyword "deny")
|
||
|
// default:
|
||
|
// diags = append(diags, &hcl.Diagnostic{
|
||
|
// // ... "invalid keyword" diagnostic message ...
|
||
|
// })
|
||
|
// }
|
||
|
//
|
||
|
// The above approach will generate the same message for both the use of an
|
||
|
// unrecognized keyword and for not using a keyword at all, which is usually
|
||
|
// reasonable if the message specifies that the given value must be a keyword
|
||
|
// from that fixed list.
|
||
|
//
|
||
|
// Note that in the native syntax the keywords "true", "false", and "null" are
|
||
|
// recognized as literal values during parsing and so these reserved words
|
||
|
// cannot not be accepted as keywords by this function.
|
||
|
//
|
||
|
// Since interpreting an expression as a keyword bypasses usual expression
|
||
|
// evaluation, it should be used sparingly for situations where e.g. one of
|
||
|
// a fixed set of keywords is used in a structural way in a special attribute
|
||
|
// to affect the further processing of a block.
|
||
|
func ExprAsKeyword(expr Expression) string {
|
||
|
type asTraversal interface {
|
||
|
AsTraversal() Traversal
|
||
|
}
|
||
|
|
||
|
physExpr := UnwrapExpressionUntil(expr, func(expr Expression) bool {
|
||
|
_, supported := expr.(asTraversal)
|
||
|
return supported
|
||
|
})
|
||
|
|
||
|
if asT, supported := physExpr.(asTraversal); supported {
|
||
|
if traversal := asT.AsTraversal(); len(traversal) == 1 {
|
||
|
return traversal.RootName()
|
||
|
}
|
||
|
}
|
||
|
return ""
|
||
|
}
|