json: Add json.ParseWithStartPos function
This is like json.Parse but allows specifying a non-default start position, in case the caller is parsing a fragment from a larger JSON document.
This commit is contained in:
parent
b41530cb36
commit
e72341df8a
@ -8,15 +8,8 @@ import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
func parseFileContent(buf []byte, filename string) (node, hcl.Diagnostics) {
|
||||
tokens := scan(buf, pos{
|
||||
Filename: filename,
|
||||
Pos: hcl.Pos{
|
||||
Byte: 0,
|
||||
Line: 1,
|
||||
Column: 1,
|
||||
},
|
||||
})
|
||||
func parseFileContent(buf []byte, filename string, start hcl.Pos) (node, hcl.Diagnostics) {
|
||||
tokens := scan(buf, pos{Filename: filename, Pos: start})
|
||||
p := newPeeker(tokens)
|
||||
node, diags := parseValue(p)
|
||||
if len(diags) == 0 && p.Peek().Type != tokenEOF {
|
||||
|
@ -598,7 +598,49 @@ func TestParse(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.Input, func(t *testing.T) {
|
||||
got, diag := parseFileContent([]byte(test.Input), "")
|
||||
got, diag := parseFileContent([]byte(test.Input), "", hcl.Pos{Byte: 0, Line: 1, Column: 1})
|
||||
|
||||
if len(diag) != test.DiagCount {
|
||||
t.Errorf("got %d diagnostics; want %d", len(diag), test.DiagCount)
|
||||
for _, d := range diag {
|
||||
t.Logf(" - %s", d.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if diff := deep.Equal(got, test.Want); diff != nil {
|
||||
for _, problem := range diff {
|
||||
t.Error(problem)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseWithPos(t *testing.T) {
|
||||
tests := []struct {
|
||||
Input string
|
||||
StartPos hcl.Pos
|
||||
Want node
|
||||
DiagCount int
|
||||
}{
|
||||
// Simple, single-token constructs
|
||||
{
|
||||
`true`,
|
||||
hcl.Pos{Byte: 0, Line: 3, Column: 10},
|
||||
&booleanVal{
|
||||
Value: true,
|
||||
SrcRange: hcl.Range{
|
||||
Start: hcl.Pos{Line: 3, Column: 10, Byte: 0},
|
||||
End: hcl.Pos{Line: 3, Column: 14, Byte: 4},
|
||||
},
|
||||
},
|
||||
0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.Input, func(t *testing.T) {
|
||||
got, diag := parseFileContent([]byte(test.Input), "", test.StartPos)
|
||||
|
||||
if len(diag) != test.DiagCount {
|
||||
t.Errorf("got %d diagnostics; want %d", len(diag), test.DiagCount)
|
||||
|
@ -18,7 +18,16 @@ import (
|
||||
// from its HasErrors method. If HasErrors returns true, the file represents
|
||||
// the subset of data that was able to be parsed, which may be none.
|
||||
func Parse(src []byte, filename string) (*hcl.File, hcl.Diagnostics) {
|
||||
rootNode, diags := parseFileContent(src, filename)
|
||||
return ParseWithStartPos(src, filename, hcl.Pos{Byte: 0, Line: 1, Column: 1})
|
||||
}
|
||||
|
||||
// ParseWithStartPos attempts to parse like json.Parse, but unlike json.Parse
|
||||
// you can pass a start position of the given JSON as a hcl.Pos.
|
||||
//
|
||||
// In most cases json.Parse should be sufficient, but it can be useful for parsing
|
||||
// a part of JSON with correct positions.
|
||||
func ParseWithStartPos(src []byte, filename string, start hcl.Pos) (*hcl.File, hcl.Diagnostics) {
|
||||
rootNode, diags := parseFileContent(src, filename, start)
|
||||
|
||||
switch rootNode.(type) {
|
||||
case *objectVal, *arrayVal:
|
||||
|
@ -112,3 +112,73 @@ func TestParse_malformed(t *testing.T) {
|
||||
t.Errorf("got nil File; want actual file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseWithStartPos(t *testing.T) {
|
||||
src := `{
|
||||
"foo": {
|
||||
"bar": "baz"
|
||||
}
|
||||
}`
|
||||
part := `{
|
||||
"bar": "baz"
|
||||
}`
|
||||
|
||||
file, diags := Parse([]byte(src), "")
|
||||
partFile, partDiags := ParseWithStartPos([]byte(part), "", hcl.Pos{Byte: 0, Line: 2, Column: 10})
|
||||
if len(diags) != 0 {
|
||||
t.Errorf("got %d diagnostics on parse src; want 0", len(diags))
|
||||
for _, diag := range diags {
|
||||
t.Logf("- %s", diag.Error())
|
||||
}
|
||||
}
|
||||
if len(partDiags) != 0 {
|
||||
t.Errorf("got %d diagnostics on parse part src; want 0", len(partDiags))
|
||||
for _, diag := range partDiags {
|
||||
t.Logf("- %s", diag.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if file == nil {
|
||||
t.Errorf("got nil File; want actual file")
|
||||
}
|
||||
if file.Body == nil {
|
||||
t.Fatalf("got nil Body; want actual body")
|
||||
}
|
||||
if partFile == nil {
|
||||
t.Errorf("got nil part File; want actual file")
|
||||
}
|
||||
if partFile.Body == nil {
|
||||
t.Fatalf("got nil part Body; want actual body")
|
||||
}
|
||||
|
||||
content, diags := file.Body.Content(&hcl.BodySchema{
|
||||
Blocks: []hcl.BlockHeaderSchema{{Type: "foo"}},
|
||||
})
|
||||
if len(diags) != 0 {
|
||||
t.Errorf("got %d diagnostics on decode; want 0", len(diags))
|
||||
for _, diag := range diags {
|
||||
t.Logf("- %s", diag.Error())
|
||||
}
|
||||
}
|
||||
attrs, diags := content.Blocks[0].Body.JustAttributes()
|
||||
if len(diags) != 0 {
|
||||
t.Errorf("got %d diagnostics on decode; want 0", len(diags))
|
||||
for _, diag := range diags {
|
||||
t.Logf("- %s", diag.Error())
|
||||
}
|
||||
}
|
||||
srcRange := attrs["bar"].Expr.Range()
|
||||
|
||||
partAttrs, diags := partFile.Body.JustAttributes()
|
||||
if len(diags) != 0 {
|
||||
t.Errorf("got %d diagnostics on decode; want 0", len(diags))
|
||||
for _, diag := range diags {
|
||||
t.Logf("- %s", diag.Error())
|
||||
}
|
||||
}
|
||||
partRange := partAttrs["bar"].Expr.Range()
|
||||
|
||||
if srcRange.String() != partRange.String() {
|
||||
t.Errorf("The two ranges did not match: src=%s, part=%s", srcRange, partRange)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user