Repository: ariga/atlas
Pull request #279 (2021-12-05 12:45:57)

schema/schemaspec/schemahcl: support setting the evaluation context for hcl


Patch:
diff --git a/schema/schemaspec/schemahcl/context.go b/schema/schemaspec/schemahcl/context.go
index 00a97e1d..56c8d8df 100644
--- a/schema/schemaspec/schemahcl/context.go
+++ b/schema/schemaspec/schemahcl/context.go
@@ -27,7 +27,7 @@ import (
 //		title = "{greeting.hebrew.word}, world!"
 //	}
 //
-func evalCtx(f *hcl.File) (*hcl.EvalContext, error) {
+func evalCtx(ctx *hcl.EvalContext, f *hcl.File) (*hcl.EvalContext, error) {
 	c := &container{}
 	if diag := gohcl.DecodeBody(f.Body, &hcl.EvalContext{}, c); diag.HasErrors() {
 		return nil, diag
@@ -41,9 +41,10 @@ func evalCtx(f *hcl.File) (*hcl.EvalContext, error) {
 	if err != nil {
 		return nil, err
 	}
-	return &hcl.EvalContext{
-		Variables: vars,
-	}, nil
+	for k, v := range vars {
+		ctx.Variables[k] = v
+	}
+	return ctx, nil
 }
 
 func blockVars(b *hclsyntax.Body, parentAddr string, defs *blockDef) (map[string]cty.Value, error) {
diff --git a/schema/schemaspec/schemahcl/hcl.go b/schema/schemaspec/schemahcl/hcl.go
index d06495a9..3adfe6ba 100644
--- a/schema/schemaspec/schemahcl/hcl.go
+++ b/schema/schemaspec/schemahcl/hcl.go
@@ -30,24 +30,34 @@ var (
 
 var (
 	// Unmarshal parses the Atlas HCL-encoded data and stores the result in the target.
-	Unmarshal = schemaspec.UnmarshalerFunc(func(data []byte, v interface{}) error {
-		spec, err := decode(data)
-		if err != nil {
-			return fmt.Errorf("schemahcl: failed decoding: %w", err)
-		}
-		if err := spec.As(v); err != nil {
-			return fmt.Errorf("schemahcl: failed reading spec as %T: %w", v, err)
-		}
-		return nil
-	})
+	Unmarshal = schemaspec.UnmarshalerFunc(NewUnmarshaler().UnmarshalSpec)
 )
 
-type container struct {
-	Body hcl.Body `hcl:",remain"`
+type (
+	container struct {
+		Body hcl.Body `hcl:",remain"`
+	}
+
+	// Unmarshaler implements schemaspec.Unmarshaler
+	Unmarshaler struct {
+		config *Config
+	}
+)
+
+// UnmarshalSpec implements schemaspec.Unmarshaler.
+func (u *Unmarshaler) UnmarshalSpec(data []byte, v interface{}) error {
+	spec, err := decode(u.config.ctx, data)
+	if err != nil {
+		return fmt.Errorf("schemahcl: failed decoding: %w", err)
+	}
+	if err := spec.As(v); err != nil {
+		return fmt.Errorf("schemahcl: failed reading spec as %T: %w", v, err)
+	}
+	return nil
 }
 
 // decode decodes the input Atlas HCL document and returns a *schemaspec.Resource representing it.
-func decode(body []byte) (*schemaspec.Resource, error) {
+func decode(ctx *hcl.EvalContext, body []byte) (*schemaspec.Resource, error) {
 	parser := hclparse.NewParser()
 	srcHCL, diag := parser.ParseHCL(body, "")
 	if diag.HasErrors() {
@@ -57,7 +67,7 @@ func decode(body []byte) (*schemaspec.Resource, error) {
 		return nil, fmt.Errorf("schemahcl: no HCL syntax found in body")
 	}
 	c := &container{}
-	ctx, err := evalCtx(srcHCL)
+	ctx, err := evalCtx(ctx, srcHCL)
 	if err != nil {
 		return nil, err
 	}
diff --git a/schema/schemaspec/schemahcl/opts.go b/schema/schemaspec/schemahcl/opts.go
new file mode 100644
index 00000000..dfdd7d13
--- /dev/null
+++ b/schema/schemaspec/schemahcl/opts.go
@@ -0,0 +1,36 @@
+package schemahcl
+
+import (
+	"github.com/hashicorp/hcl/v2"
+	"github.com/zclconf/go-cty/cty"
+)
+
+type (
+	// Config configures an unmarshaling.
+	Config struct {
+		ctx *hcl.EvalContext
+	}
+
+	// Option configures a Config.
+	Option func(*Config)
+)
+
+// NewUnmarshaler returns a schemaspec.Unmarshaler configured with options.
+func NewUnmarshaler(opts ...Option) *Unmarshaler {
+	cfg := &Config{
+		ctx: &hcl.EvalContext{
+			Variables: make(map[string]cty.Value),
+		},
+	}
+	for _, opt := range opts {
+		opt(cfg)
+	}
+	return &Unmarshaler{config: cfg}
+}
+
+// EvalContext configures an unmarshaler to decode with an *hcl.EvalContext.
+func EvalContext(ctx *hcl.EvalContext) Option {
+	return func(config *Config) {
+		config.ctx = ctx
+	}
+}


Test patch:
diff --git a/schema/schemaspec/schemahcl/context_test.go b/schema/schemaspec/schemahcl/context_test.go
index 6eea3f0f..7a9f58f4 100644
--- a/schema/schemaspec/schemahcl/context_test.go
+++ b/schema/schemaspec/schemahcl/context_test.go
@@ -4,7 +4,9 @@ import (
 	"testing"
 
 	"ariga.io/atlas/schema/schemaspec"
+	"github.com/hashicorp/hcl/v2"
 	"github.com/stretchr/testify/require"
+	"github.com/zclconf/go-cty/cty"
 )
 
 func TestReferences(t *testing.T) {
@@ -328,3 +330,22 @@ point {
 `
 	require.Equal(t, expected, string(b))
 }
+
+func TestEvalContext(t *testing.T) {
+	f := `
+x = from_ctx
+`
+	var test struct {
+		X int `spec:"x"`
+	}
+	unmarshaler := NewUnmarshaler(
+		EvalContext(&hcl.EvalContext{
+			Variables: map[string]cty.Value{
+				"from_ctx": cty.NumberIntVal(42),
+			},
+		}),
+	)
+	err := unmarshaler.UnmarshalSpec([]byte(f), &test)
+	require.NoError(t, err)
+	require.EqualValues(t, 42, test.X)
+}