hclpack: PackNativeFile to get a packed version of a native syntax file
This is a straightforward way to get a hclpack.Body in the common case where the input is already native syntax source code. Since the native syntax is unambiguous about structure, the whole structure can be packed in a single pass with no further information.
This commit is contained in:
parent
227ba9e86b
commit
ed7453e277
58
hclpack/pack_native.go
Normal file
58
hclpack/pack_native.go
Normal file
@ -0,0 +1,58 @@
|
||||
package hclpack
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl2/hcl"
|
||||
"github.com/hashicorp/hcl2/hcl/hclsyntax"
|
||||
)
|
||||
|
||||
// PackNativeFile parses the given source code as HCL native syntax and packs
|
||||
// it into a hclpack Body ready to be marshalled.
|
||||
//
|
||||
// If the given source code contains syntax errors then error diagnostics will
|
||||
// be returned. A non-nil body might still be returned in this case, which
|
||||
// allows a cautious caller to still do certain analyses on the result.
|
||||
func PackNativeFile(src []byte, filename string, start hcl.Pos) (*Body, hcl.Diagnostics) {
|
||||
f, diags := hclsyntax.ParseConfig(src, filename, start)
|
||||
rootBody := f.Body.(*hclsyntax.Body)
|
||||
return packNativeBody(rootBody, src), diags
|
||||
}
|
||||
|
||||
func packNativeBody(body *hclsyntax.Body, src []byte) *Body {
|
||||
ret := &Body{}
|
||||
for name, attr := range body.Attributes {
|
||||
exprRng := attr.Expr.Range()
|
||||
exprStartRng := attr.Expr.StartRange()
|
||||
exprSrc := exprRng.SliceBytes(src)
|
||||
ret.setAttribute(name, Attribute{
|
||||
Expr: Expression{
|
||||
Source: exprSrc,
|
||||
SourceType: ExprNative,
|
||||
|
||||
Range_: exprRng,
|
||||
StartRange_: exprStartRng,
|
||||
},
|
||||
Range: attr.Range(),
|
||||
NameRange: attr.NameRange,
|
||||
})
|
||||
}
|
||||
|
||||
for _, block := range body.Blocks {
|
||||
childBody := packNativeBody(block.Body, src)
|
||||
defRange := block.TypeRange
|
||||
if len(block.LabelRanges) > 0 {
|
||||
defRange = hcl.RangeBetween(defRange, block.LabelRanges[len(block.LabelRanges)-1])
|
||||
}
|
||||
ret.appendBlock(Block{
|
||||
Type: block.Type,
|
||||
Labels: block.Labels,
|
||||
Body: *childBody,
|
||||
TypeRange: block.TypeRange,
|
||||
DefRange: defRange,
|
||||
LabelRanges: block.LabelRanges,
|
||||
})
|
||||
}
|
||||
|
||||
ret.MissingItemRange_ = body.EndRange
|
||||
|
||||
return ret
|
||||
}
|
166
hclpack/pack_native_test.go
Normal file
166
hclpack/pack_native_test.go
Normal file
@ -0,0 +1,166 @@
|
||||
package hclpack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
"github.com/hashicorp/hcl2/hcl"
|
||||
)
|
||||
|
||||
func TestPackNativeFile(t *testing.T) {
|
||||
src := `
|
||||
foo = "bar"
|
||||
baz = "boz"
|
||||
|
||||
child {
|
||||
a = b + c
|
||||
}
|
||||
|
||||
another_child "foo" "bar" {}
|
||||
`
|
||||
|
||||
got, diags := PackNativeFile([]byte(src), "", hcl.Pos{Line: 1, Column: 1})
|
||||
for _, diag := range diags {
|
||||
t.Errorf("unexpected diagnostic: %s", diag.Error())
|
||||
}
|
||||
|
||||
want := &Body{
|
||||
Attributes: map[string]Attribute{
|
||||
"baz": {
|
||||
Expr: Expression{
|
||||
Source: []byte(`"boz"`),
|
||||
SourceType: ExprNative,
|
||||
Range_: hcl.Range{
|
||||
Start: hcl.Pos{Line: 3, Column: 7, Byte: 19},
|
||||
End: hcl.Pos{Line: 3, Column: 12, Byte: 24},
|
||||
},
|
||||
StartRange_: hcl.Range{
|
||||
Start: hcl.Pos{Line: 3, Column: 8, Byte: 20},
|
||||
End: hcl.Pos{Line: 3, Column: 11, Byte: 23},
|
||||
},
|
||||
},
|
||||
Range: hcl.Range{
|
||||
Start: hcl.Pos{Line: 3, Column: 1, Byte: 13},
|
||||
End: hcl.Pos{Line: 3, Column: 12, Byte: 24},
|
||||
},
|
||||
NameRange: hcl.Range{
|
||||
Start: hcl.Pos{Line: 3, Column: 1, Byte: 13},
|
||||
End: hcl.Pos{Line: 3, Column: 4, Byte: 16},
|
||||
},
|
||||
},
|
||||
"foo": {
|
||||
Expr: Expression{
|
||||
Source: []byte(`"bar"`),
|
||||
SourceType: ExprNative,
|
||||
Range_: hcl.Range{
|
||||
Start: hcl.Pos{Line: 2, Column: 7, Byte: 7},
|
||||
End: hcl.Pos{Line: 2, Column: 12, Byte: 12},
|
||||
},
|
||||
StartRange_: hcl.Range{
|
||||
Start: hcl.Pos{Line: 2, Column: 8, Byte: 8},
|
||||
End: hcl.Pos{Line: 2, Column: 11, Byte: 11},
|
||||
},
|
||||
},
|
||||
Range: hcl.Range{
|
||||
Start: hcl.Pos{Line: 2, Column: 1, Byte: 1},
|
||||
End: hcl.Pos{Line: 2, Column: 12, Byte: 12},
|
||||
},
|
||||
NameRange: hcl.Range{
|
||||
Start: hcl.Pos{Line: 2, Column: 1, Byte: 1},
|
||||
End: hcl.Pos{Line: 2, Column: 4, Byte: 4},
|
||||
},
|
||||
},
|
||||
},
|
||||
ChildBlocks: []Block{
|
||||
{
|
||||
Type: "child",
|
||||
Body: Body{
|
||||
Attributes: map[string]Attribute{
|
||||
"a": {
|
||||
Expr: Expression{
|
||||
Source: []byte(`b + c`),
|
||||
SourceType: ExprNative,
|
||||
Range_: hcl.Range{
|
||||
Start: hcl.Pos{Line: 6, Column: 7, Byte: 40},
|
||||
End: hcl.Pos{Line: 6, Column: 12, Byte: 45},
|
||||
},
|
||||
StartRange_: hcl.Range{
|
||||
Start: hcl.Pos{Line: 6, Column: 7, Byte: 40},
|
||||
End: hcl.Pos{Line: 6, Column: 8, Byte: 41},
|
||||
},
|
||||
},
|
||||
Range: hcl.Range{
|
||||
Start: hcl.Pos{Line: 6, Column: 3, Byte: 36},
|
||||
End: hcl.Pos{Line: 6, Column: 12, Byte: 45},
|
||||
},
|
||||
NameRange: hcl.Range{
|
||||
Start: hcl.Pos{Line: 6, Column: 3, Byte: 36},
|
||||
End: hcl.Pos{Line: 6, Column: 4, Byte: 37},
|
||||
},
|
||||
},
|
||||
},
|
||||
MissingItemRange_: hcl.Range{
|
||||
Start: hcl.Pos{Line: 7, Column: 2, Byte: 47},
|
||||
End: hcl.Pos{Line: 7, Column: 2, Byte: 47},
|
||||
},
|
||||
},
|
||||
DefRange: hcl.Range{
|
||||
Start: hcl.Pos{Line: 5, Column: 1, Byte: 26},
|
||||
End: hcl.Pos{Line: 5, Column: 6, Byte: 31},
|
||||
},
|
||||
TypeRange: hcl.Range{
|
||||
Start: hcl.Pos{Line: 5, Column: 1, Byte: 26},
|
||||
End: hcl.Pos{Line: 5, Column: 6, Byte: 31},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "another_child",
|
||||
Labels: []string{"foo", "bar"},
|
||||
Body: Body{
|
||||
MissingItemRange_: hcl.Range{
|
||||
Start: hcl.Pos{Line: 9, Column: 29, Byte: 77},
|
||||
End: hcl.Pos{Line: 9, Column: 29, Byte: 77},
|
||||
},
|
||||
},
|
||||
DefRange: hcl.Range{
|
||||
Start: hcl.Pos{Line: 9, Column: 1, Byte: 49},
|
||||
End: hcl.Pos{Line: 9, Column: 26, Byte: 74},
|
||||
},
|
||||
TypeRange: hcl.Range{
|
||||
Start: hcl.Pos{Line: 9, Column: 1, Byte: 49},
|
||||
End: hcl.Pos{Line: 9, Column: 14, Byte: 62},
|
||||
},
|
||||
LabelRanges: []hcl.Range{
|
||||
hcl.Range{
|
||||
Start: hcl.Pos{Line: 9, Column: 15, Byte: 63},
|
||||
End: hcl.Pos{Line: 9, Column: 20, Byte: 68},
|
||||
},
|
||||
hcl.Range{
|
||||
Start: hcl.Pos{Line: 9, Column: 21, Byte: 69},
|
||||
End: hcl.Pos{Line: 9, Column: 26, Byte: 74},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MissingItemRange_: hcl.Range{
|
||||
Start: hcl.Pos{Line: 10, Column: 1, Byte: 78},
|
||||
End: hcl.Pos{Line: 10, Column: 1, Byte: 78},
|
||||
},
|
||||
}
|
||||
|
||||
if !cmp.Equal(want, got) {
|
||||
bytesAsString := func(s []byte) string {
|
||||
return string(s)
|
||||
}
|
||||
posAsString := func(pos hcl.Pos) string {
|
||||
return fmt.Sprintf("%#v", pos)
|
||||
}
|
||||
t.Errorf("wrong result\n%s", cmp.Diff(
|
||||
want, got,
|
||||
cmp.Transformer("bytesAsString", bytesAsString),
|
||||
cmp.Transformer("posAsString", posAsString),
|
||||
))
|
||||
}
|
||||
}
|
@ -17,6 +17,10 @@ type Body struct {
|
||||
var _ hcl.Body = (*Body)(nil)
|
||||
|
||||
// Content is an implementation of the method of the same name on hcl.Body.
|
||||
//
|
||||
// When Content is called directly on a hclpack.Body, all child block bodies
|
||||
// are guaranteed to be of type *hclpack.Body, so callers can type-assert
|
||||
// to obtain a child Body in order to serialize it separately if needed.
|
||||
func (b *Body) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
|
||||
return b.content(schema, nil)
|
||||
}
|
||||
@ -26,6 +30,11 @@ func (b *Body) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostic
|
||||
// The returned "remain" body may share some backing objects with the receiver,
|
||||
// so neither the receiver nor the returned remain body, or any descendent
|
||||
// objects within them, may be mutated after this method is used.
|
||||
//
|
||||
// When Content is called directly on a hclpack.Body, all child block bodies
|
||||
// and the returned "remain" body are guaranteed to be of type *hclpack.Body,
|
||||
// so callers can type-assert to obtain a child Body in order to serialize it
|
||||
// separately if needed.
|
||||
func (b *Body) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
|
||||
remain := &Body{}
|
||||
content, diags := b.content(schema, remain)
|
||||
|
Loading…
Reference in New Issue
Block a user