From 160f56abb19aa2a5af98bca1136f6e253eabc0a8 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Thu, 18 May 2017 07:57:04 -0700 Subject: [PATCH] json: beginnings of parsing JSON into File objects The returned file objects are not actually functional yet, but this establishes the interface. --- zcl/json/public.go | 70 +++++++++++++++++++++++++++++++++++++++++ zcl/json/public_test.go | 16 ++++++++++ zcl/json/structure.go | 32 +++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 zcl/json/public.go create mode 100644 zcl/json/public_test.go create mode 100644 zcl/json/structure.go diff --git a/zcl/json/public.go b/zcl/json/public.go new file mode 100644 index 0000000..c2f0285 --- /dev/null +++ b/zcl/json/public.go @@ -0,0 +1,70 @@ +package json + +import ( + "fmt" + "io/ioutil" + "os" + + "github.com/apparentlymart/go-zcl/zcl" +) + +// Parse attempts to parse the given buffer as JSON and, if successful, returns +// a zcl.File for the zcl configuration represented by it. +// +// This is not a generic JSON parser. Instead, it deals only with the profile +// of JSON used to express zcl configuration. +// +// The returned file is valid if it is non-nil, regardless of whether the +// diagnostics are also non-nil. If both are returned, the diagnostics should +// still be presented to the user because they may contain warnings. +func Parse(src []byte, filename string) (*zcl.File, zcl.Diagnostics) { + var file *zcl.File + rootNode, diags := parseFileContent(src, filename) + if _, ok := rootNode.(*objectVal); !ok { + return nil, diags.Append(&zcl.Diagnostic{ + Severity: zcl.DiagError, + Summary: "Root value must be object", + Detail: "The root value in a JSON-based configuration must be a JSON object.", + Subject: rootNode.StartRange().Ptr(), + }) + } + if rootNode != nil { + file = &zcl.File{ + Body: &body{ + obj: rootNode.(*objectVal), + }, + } + } + return file, diags +} + +// ParseFile is a convenience wrapper around Parse that first attempts to load +// data from the given filename, passing the result to Parse if successful. +// +// If the file cannot be read, an error diagnostic with nil context is returned. +func ParseFile(filename string) (*zcl.File, zcl.Diagnostics) { + f, err := os.Open(filename) + if err != nil { + return nil, zcl.Diagnostics{ + { + Severity: zcl.DiagError, + Summary: "Failed to open file", + Detail: fmt.Sprintf("The file %q could not be opened.", filename), + }, + } + } + defer f.Close() + + src, err := ioutil.ReadAll(f) + if err != nil { + return nil, zcl.Diagnostics{ + { + Severity: zcl.DiagError, + Summary: "Failed to read file", + Detail: fmt.Sprintf("The file %q was opened, but an error occured while reading it.", filename), + }, + } + } + + return Parse(src, filename) +} diff --git a/zcl/json/public_test.go b/zcl/json/public_test.go new file mode 100644 index 0000000..4949764 --- /dev/null +++ b/zcl/json/public_test.go @@ -0,0 +1,16 @@ +package json + +import ( + "testing" +) + +func TestParse_nonObject(t *testing.T) { + src := `true` + file, diags := Parse([]byte(src), "") + if len(diags) != 1 { + t.Errorf("got %d diagnostics; want 1", len(diags)) + } + if file != nil { + t.Errorf("got non-nil File; want nil") + } +} diff --git a/zcl/json/structure.go b/zcl/json/structure.go new file mode 100644 index 0000000..a3aeee9 --- /dev/null +++ b/zcl/json/structure.go @@ -0,0 +1,32 @@ +package json + +import ( + "github.com/apparentlymart/go-zcl/zcl" +) + +// body is the implementation of "Body" used for files processed with the JSON +// parser. +type body struct { + obj *objectVal + + // If non-nil, the keys of this map cause the corresponding attributes to + // be treated as non-existing. This is used when Body.PartialContent is + // called, to produce the "remaining content" Body. + hiddenAttrs map[string]struct{} +} + +// expression is the implementation of "Expression" used for files processed +// with the JSON parser. +type expression struct { + src node +} + +func (b *body) Content(schema *zcl.BodySchema) (*zcl.BodyContent, zcl.Diagnostics) { + // TODO: Implement + return nil, nil +} + +func (b *body) PartialContent(schema *zcl.BodySchema) (*zcl.BodyContent, zcl.Body, zcl.Diagnostics) { + // TODO: Implement + return nil, nil, nil +}