github.com/hashicorp/hcl/v2@v2.20.0/expr_unwrap.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package hcl 5 6 type unwrapExpression interface { 7 UnwrapExpression() Expression 8 } 9 10 // UnwrapExpression removes any "wrapper" expressions from the given expression, 11 // to recover the representation of the physical expression given in source 12 // code. 13 // 14 // Sometimes wrapping expressions are used to modify expression behavior, e.g. 15 // in extensions that need to make some local variables available to certain 16 // sub-trees of the configuration. This can make it difficult to reliably 17 // type-assert on the physical AST types used by the underlying syntax. 18 // 19 // Unwrapping an expression may modify its behavior by stripping away any 20 // additional constraints or capabilities being applied to the Value and 21 // Variables methods, so this function should generally only be used prior 22 // to operations that concern themselves with the static syntax of the input 23 // configuration, and not with the effective value of the expression. 24 // 25 // Wrapper expression types must support unwrapping by implementing a method 26 // called UnwrapExpression that takes no arguments and returns the embedded 27 // Expression. Implementations of this method should peel away only one level 28 // of wrapping, if multiple are present. This method may return nil to 29 // indicate _dynamically_ that no wrapped expression is available, for 30 // expression types that might only behave as wrappers in certain cases. 31 func UnwrapExpression(expr Expression) Expression { 32 for { 33 unwrap, wrapped := expr.(unwrapExpression) 34 if !wrapped { 35 return expr 36 } 37 innerExpr := unwrap.UnwrapExpression() 38 if innerExpr == nil { 39 return expr 40 } 41 expr = innerExpr 42 } 43 } 44 45 // UnwrapExpressionUntil is similar to UnwrapExpression except it gives the 46 // caller an opportunity to test each level of unwrapping to see each a 47 // particular expression is accepted. 48 // 49 // This could be used, for example, to unwrap until a particular other 50 // interface is satisfied, regardless of wrap wrapping level it is satisfied 51 // at. 52 // 53 // The given callback function must return false to continue wrapping, or 54 // true to accept and return the proposed expression given. If the callback 55 // function rejects even the final, physical expression then the result of 56 // this function is nil. 57 func UnwrapExpressionUntil(expr Expression, until func(Expression) bool) Expression { 58 for { 59 if until(expr) { 60 return expr 61 } 62 unwrap, wrapped := expr.(unwrapExpression) 63 if !wrapped { 64 return nil 65 } 66 expr = unwrap.UnwrapExpression() 67 if expr == nil { 68 return nil 69 } 70 } 71 }