5e07d8e1f9
In most applications it's possible to fully evaluate configuration at the beginning and work only with resolved values after that, but in some unusual cases it's necessary to split parsing and decoding between two separate processes connected by a pipe or network connection. hclpack is intended to provide compact wire formats for sending bodies over the network such that they can be decoded and evaluated and get the same results. This is not something that can happen fully automatically because a hcl.Body is an abstract node rather than a physical construct, and so access to the original source code is required to construct such a representation, and to interpret any source ranges that emerged from the final evaluation.
105 lines
3.5 KiB
Go
105 lines
3.5 KiB
Go
package hclpack
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/hcl2/hcl"
|
|
"github.com/hashicorp/hcl2/hcl/hclsyntax"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
// Expression is an implementation of hcl.Expression in terms of some raw
|
|
// expression source code. The methods of this type will first parse the
|
|
// source code and then pass the call through to the real expression that
|
|
// is produced.
|
|
type Expression struct {
|
|
// Source is the raw source code of the expression, which should be parsed
|
|
// as the syntax specified by SourceType.
|
|
Source []byte
|
|
SourceType ExprSourceType
|
|
|
|
// Range_ and StartRange_ describe the physical extents of the expression
|
|
// in the original source code. SourceRange_ is its entire range while
|
|
// StartRange is just the tokens that introduce the expression type. For
|
|
// simple expression types, SourceRange and StartRange are identical.
|
|
Range_, StartRange_ hcl.Range
|
|
}
|
|
|
|
var _ hcl.Expression = (*Expression)(nil)
|
|
|
|
// Value implements the Value method of hcl.Expression but with the additional
|
|
// step of first parsing the expression source code. This implementation is
|
|
// unusual in that it can potentially return syntax errors, whereas other
|
|
// Value implementations usually work with already-parsed expressions.
|
|
func (e *Expression) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|
expr, diags := e.Parse()
|
|
if diags.HasErrors() {
|
|
return cty.DynamicVal, diags
|
|
}
|
|
|
|
val, moreDiags := expr.Value(ctx)
|
|
diags = append(diags, moreDiags...)
|
|
return val, diags
|
|
}
|
|
|
|
// Variables implements the Variables method of hcl.Expression but with the
|
|
// additional step of first parsing the expression source code.
|
|
//
|
|
// Since this method cannot return errors, it will return a nil slice if
|
|
// parsing fails, indicating that no variables are present. This is okay in
|
|
// practice because a subsequent call to Value would fail with syntax errors
|
|
// regardless of what variables are in the context.
|
|
func (e *Expression) Variables() []hcl.Traversal {
|
|
expr, diags := e.Parse()
|
|
if diags.HasErrors() {
|
|
return nil
|
|
}
|
|
return expr.Variables()
|
|
}
|
|
|
|
func (e *Expression) Range() hcl.Range {
|
|
return e.Range_
|
|
}
|
|
|
|
func (e *Expression) StartRange() hcl.Range {
|
|
return e.StartRange_
|
|
}
|
|
|
|
// Parse attempts to parse the source code of the receiving expression using
|
|
// its indicated source type, returning the expression if possible and any
|
|
// diagnostics produced during parsing.
|
|
func (e *Expression) Parse() (hcl.Expression, hcl.Diagnostics) {
|
|
switch e.SourceType {
|
|
case ExprNative:
|
|
return hclsyntax.ParseExpression(e.Source, e.Range_.Filename, e.Range_.Start)
|
|
case ExprTemplate:
|
|
return hclsyntax.ParseTemplate(e.Source, e.Range_.Filename, e.Range_.Start)
|
|
default:
|
|
// This should never happen for a valid Expression.
|
|
return nil, hcl.Diagnostics{
|
|
{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid expression source type",
|
|
Detail: fmt.Sprintf("Packed version of this expression has an invalid source type %s. This is always a bug.", e.SourceType),
|
|
Subject: &e.Range_,
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
// ExprSourceType defines the syntax type used for an expression's source code,
|
|
// which is then used to select a suitable parser for it when evaluating.
|
|
type ExprSourceType rune
|
|
|
|
//go:generate stringer -type ExprSourceType
|
|
|
|
const (
|
|
// ExprNative indicates that an expression must be parsed as native
|
|
// expression syntax, with hclsyntax.ParseExpression.
|
|
ExprNative ExprSourceType = 'N'
|
|
|
|
// ExprTemplate indicates that an expression must be parsed as nave
|
|
// template syntax, with hclsyntax.ParseTemplate.
|
|
ExprTemplate ExprSourceType = 'T'
|
|
)
|