diff --git a/ext/include/doc.go b/ext/include/doc.go deleted file mode 100644 index 11ea07d..0000000 --- a/ext/include/doc.go +++ /dev/null @@ -1,12 +0,0 @@ -// Package include implements a HCL extension that allows inclusion of -// one HCL body into another using blocks of type "include", with the following -// structure: -// -// include { -// path = "./foo.hcl" -// } -// -// The processing of the given path is delegated to the calling application, -// allowing it to decide how to interpret the path and which syntaxes to -// support for referenced files. -package include diff --git a/ext/include/file_resolver.go b/ext/include/file_resolver.go deleted file mode 100644 index 11091b8..0000000 --- a/ext/include/file_resolver.go +++ /dev/null @@ -1,52 +0,0 @@ -package include - -import ( - "path/filepath" - "strings" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclparse" -) - -// FileResolver creates and returns a Resolver that interprets include paths -// as filesystem paths relative to the calling configuration file. -// -// When an include is requested, the source filename of the calling config -// file is first interpreted relative to the given basePath, and then the -// path given in configuration is interpreted relative to the resulting -// absolute caller configuration directory. -// -// This resolver assumes that all calling bodies are loaded from local files -// and that the paths to these files were correctly provided to the parser, -// either absolute or relative to the given basePath. -// -// If the path given in configuration ends with ".json" then the referenced -// file is interpreted as JSON. Otherwise, it is interpreted as HCL native -// syntax. -func FileResolver(baseDir string, parser *hclparse.Parser) Resolver { - return &fileResolver{ - BaseDir: baseDir, - Parser: parser, - } -} - -type fileResolver struct { - BaseDir string - Parser *hclparse.Parser -} - -func (r fileResolver) ResolveBodyPath(path string, refRange hcl.Range) (hcl.Body, hcl.Diagnostics) { - callerFile := filepath.Join(r.BaseDir, refRange.Filename) - callerDir := filepath.Dir(callerFile) - targetFile := filepath.Join(callerDir, path) - - var f *hcl.File - var diags hcl.Diagnostics - if strings.HasSuffix(targetFile, ".json") { - f, diags = r.Parser.ParseJSONFile(targetFile) - } else { - f, diags = r.Parser.ParseHCLFile(targetFile) - } - - return f.Body, diags -} diff --git a/ext/include/map_resolver.go b/ext/include/map_resolver.go deleted file mode 100644 index 452a6d8..0000000 --- a/ext/include/map_resolver.go +++ /dev/null @@ -1,29 +0,0 @@ -package include - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2" -) - -// MapResolver returns a Resolver that consults the given map for preloaded -// bodies (the values) associated with static include paths (the keys). -// -// An error diagnostic is returned if a path is requested that does not appear -// as a key in the given map. -func MapResolver(m map[string]hcl.Body) Resolver { - return ResolverFunc(func(path string, refRange hcl.Range) (hcl.Body, hcl.Diagnostics) { - if body, ok := m[path]; ok { - return body, nil - } - - return nil, hcl.Diagnostics{ - { - Severity: hcl.DiagError, - Summary: "Invalid include path", - Detail: fmt.Sprintf("The include path %q is not recognized.", path), - Subject: &refRange, - }, - } - }) -} diff --git a/ext/include/resolver.go b/ext/include/resolver.go deleted file mode 100644 index 38c8b73..0000000 --- a/ext/include/resolver.go +++ /dev/null @@ -1,28 +0,0 @@ -package include - -import ( - "github.com/hashicorp/hcl/v2" -) - -// A Resolver maps an include path (an arbitrary string, but usually something -// filepath-like) to a hcl.Body. -// -// The parameter "refRange" is the source range of the expression in the calling -// body that provided the given path, for use in generating "invalid path"-type -// diagnostics. -// -// If the returned body is nil, it will be ignored. -// -// Any returned diagnostics will be emitted when content is requested from the -// final composed body (after all includes have been dealt with). -type Resolver interface { - ResolveBodyPath(path string, refRange hcl.Range) (hcl.Body, hcl.Diagnostics) -} - -// ResolverFunc is a function type that implements Resolver. -type ResolverFunc func(path string, refRange hcl.Range) (hcl.Body, hcl.Diagnostics) - -// ResolveBodyPath is an implementation of Resolver.ResolveBodyPath. -func (f ResolverFunc) ResolveBodyPath(path string, refRange hcl.Range) (hcl.Body, hcl.Diagnostics) { - return f(path, refRange) -} diff --git a/ext/include/transformer.go b/ext/include/transformer.go deleted file mode 100644 index ae0a808..0000000 --- a/ext/include/transformer.go +++ /dev/null @@ -1,92 +0,0 @@ -package include - -import ( - "github.com/hashicorp/hcl/v2/ext/transform" - "github.com/hashicorp/hcl/v2/gohcl" - "github.com/hashicorp/hcl/v2" -) - -// Transformer builds a transformer that finds any "include" blocks in a body -// and produces a merged body that contains the original content plus the -// content of the other bodies referenced by the include blocks. -// -// blockType specifies the type of block to interpret. The conventional type name -// is "include". -// -// ctx provides an evaluation context for the path expressions in include blocks. -// If nil, path expressions may not reference variables nor functions. -// -// The given resolver is used to translate path strings (after expression -// evaluation) into bodies. FileResolver returns a reasonable implementation for -// applications that read configuration files from local disk. -// -// The returned Transformer can either be used directly to process includes -// in a shallow fashion on a single body, or it can be used with -// transform.Deep (from the sibling transform package) to allow includes -// at all levels of a nested block structure: -// -// transformer = include.Transformer("include", nil, include.FileResolver(".", parser)) -// body = transform.Deep(body, transformer) -// // "body" will now have includes resolved in its own content and that -// // of any descendent blocks. -// -func Transformer(blockType string, ctx *hcl.EvalContext, resolver Resolver) transform.Transformer { - return &transformer{ - Schema: &hcl.BodySchema{ - Blocks: []hcl.BlockHeaderSchema{ - { - Type: blockType, - }, - }, - }, - Ctx: ctx, - Resolver: resolver, - } -} - -type transformer struct { - Schema *hcl.BodySchema - Ctx *hcl.EvalContext - Resolver Resolver -} - -func (t *transformer) TransformBody(in hcl.Body) hcl.Body { - content, remain, diags := in.PartialContent(t.Schema) - - if content == nil || len(content.Blocks) == 0 { - // Nothing to do! - return transform.BodyWithDiagnostics(remain, diags) - } - - bodies := make([]hcl.Body, 1, len(content.Blocks)+1) - bodies[0] = remain // content in "remain" takes priority over includes - for _, block := range content.Blocks { - incContent, incDiags := block.Body.Content(includeBlockSchema) - diags = append(diags, incDiags...) - if incDiags.HasErrors() { - continue - } - - pathExpr := incContent.Attributes["path"].Expr - var path string - incDiags = gohcl.DecodeExpression(pathExpr, t.Ctx, &path) - diags = append(diags, incDiags...) - if incDiags.HasErrors() { - continue - } - - incBody, incDiags := t.Resolver.ResolveBodyPath(path, pathExpr.Range()) - bodies = append(bodies, transform.BodyWithDiagnostics(incBody, incDiags)) - } - - return hcl.MergeBodies(bodies) -} - -var includeBlockSchema = &hcl.BodySchema{ - Attributes: []hcl.AttributeSchema{ - { - Name: "path", - Required: true, - }, - }, -} diff --git a/ext/include/transformer_test.go b/ext/include/transformer_test.go deleted file mode 100644 index e5d6c1a..0000000 --- a/ext/include/transformer_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package include - -import ( - "reflect" - "testing" - - "github.com/davecgh/go-spew/spew" - "github.com/hashicorp/hcl/v2/gohcl" - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hcltest" - "github.com/zclconf/go-cty/cty" -) - -func TestTransformer(t *testing.T) { - caller := hcltest.MockBody(&hcl.BodyContent{ - Blocks: hcl.Blocks{ - { - Type: "include", - Body: hcltest.MockBody(&hcl.BodyContent{ - Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ - "path": hcltest.MockExprVariable("var_path"), - }), - }), - }, - { - Type: "include", - Body: hcltest.MockBody(&hcl.BodyContent{ - Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ - "path": hcltest.MockExprLiteral(cty.StringVal("include2")), - }), - }), - }, - { - Type: "foo", - Body: hcltest.MockBody(&hcl.BodyContent{ - Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ - "from": hcltest.MockExprLiteral(cty.StringVal("caller")), - }), - }), - }, - }, - }) - - resolver := MapResolver(map[string]hcl.Body{ - "include1": hcltest.MockBody(&hcl.BodyContent{ - Blocks: hcl.Blocks{ - { - Type: "foo", - Body: hcltest.MockBody(&hcl.BodyContent{ - Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ - "from": hcltest.MockExprLiteral(cty.StringVal("include1")), - }), - }), - }, - }, - }), - "include2": hcltest.MockBody(&hcl.BodyContent{ - Blocks: hcl.Blocks{ - { - Type: "foo", - Body: hcltest.MockBody(&hcl.BodyContent{ - Attributes: hcltest.MockAttrs(map[string]hcl.Expression{ - "from": hcltest.MockExprLiteral(cty.StringVal("include2")), - }), - }), - }, - }, - }), - }) - - ctx := &hcl.EvalContext{ - Variables: map[string]cty.Value{ - "var_path": cty.StringVal("include1"), - }, - } - - transformer := Transformer("include", ctx, resolver) - merged := transformer.TransformBody(caller) - - type foo struct { - From string `hcl:"from,attr"` - } - type result struct { - Foos []foo `hcl:"foo,block"` - } - var got result - diags := gohcl.DecodeBody(merged, nil, &got) - if len(diags) != 0 { - t.Errorf("unexpected diags") - for _, diag := range diags { - t.Logf("- %s", diag) - } - } - - want := result{ - Foos: []foo{ - { - From: "caller", - }, - { - From: "include1", - }, - { - From: "include2", - }, - }, - } - - if !reflect.DeepEqual(got, want) { - t.Errorf("wrong result\ngot: %swant: %s", spew.Sdump(got), spew.Sdump(want)) - } -}