summaryrefslogtreecommitdiff
path: root/cmd/hcldec
diff options
context:
space:
mode:
authorMartin Atkins <mart@degeneration.co.uk>2018-08-09 18:10:00 -0700
committerMartin Atkins <mart@degeneration.co.uk>2018-08-09 18:10:14 -0700
commit6743a2254ba3d642b7d3a0be506259a0842819ac (patch)
tree0a43e2fc0bbc35b1b55400c4efcf6baa82e1e8d1 /cmd/hcldec
parent609cc35d49710798824b43cce9622fb08d20ecd9 (diff)
cmd/hcldec: opt-in JSON-formatted diagnostics
By default we generate human-readable diagnostics on the assumption that the caller is a simple program that is capturing stdin via a pipe and letting stderr go to the terminal. More sophisticated callers may wish to analyze the diagnostics themselves and perhaps present them in a different way, such as via a GUI.
Diffstat (limited to 'cmd/hcldec')
-rw-r--r--cmd/hcldec/diags_json.go101
-rw-r--r--cmd/hcldec/main.go26
2 files changed, 120 insertions, 7 deletions
diff --git a/cmd/hcldec/diags_json.go b/cmd/hcldec/diags_json.go
new file mode 100644
index 0000000..e24181f
--- /dev/null
+++ b/cmd/hcldec/diags_json.go
@@ -0,0 +1,101 @@
+package main
+
+import (
+ "encoding/json"
+ "io"
+
+ "github.com/hashicorp/hcl2/hcl"
+)
+
+type jsonDiagWriter struct {
+ w io.Writer
+ diags hcl.Diagnostics
+}
+
+var _ hcl.DiagnosticWriter = &jsonDiagWriter{}
+
+func (wr *jsonDiagWriter) WriteDiagnostic(diag *hcl.Diagnostic) error {
+ wr.diags = append(wr.diags, diag)
+ return nil
+}
+
+func (wr *jsonDiagWriter) WriteDiagnostics(diags hcl.Diagnostics) error {
+ wr.diags = append(wr.diags, diags...)
+ return nil
+}
+
+func (wr *jsonDiagWriter) Flush() error {
+ if len(wr.diags) == 0 {
+ return nil
+ }
+
+ type PosJSON struct {
+ Line int `json:"line"`
+ Column int `json:"column"`
+ Byte int `json:"byte"`
+ }
+ type RangeJSON struct {
+ Filename string `json:"filename"`
+ Start PosJSON `json:"start"`
+ End PosJSON `json:"end"`
+ }
+ type DiagnosticJSON struct {
+ Severity string `json:"severity"`
+ Summary string `json:"summary"`
+ Detail string `json:"detail,omitempty"`
+ Subject *RangeJSON `json:"subject,omitempty"`
+ }
+ type DiagnosticsJSON struct {
+ Diagnostics []DiagnosticJSON `json:"diagnostics"`
+ }
+
+ diagsJSON := make([]DiagnosticJSON, 0, len(wr.diags))
+ for _, diag := range wr.diags {
+ var diagJSON DiagnosticJSON
+
+ switch diag.Severity {
+ case hcl.DiagError:
+ diagJSON.Severity = "error"
+ case hcl.DiagWarning:
+ diagJSON.Severity = "warning"
+ default:
+ diagJSON.Severity = "(unknown)" // should never happen
+ }
+
+ diagJSON.Summary = diag.Summary
+ diagJSON.Detail = diag.Detail
+ if diag.Subject != nil {
+ diagJSON.Subject = &RangeJSON{}
+ sJSON := diagJSON.Subject
+ rng := diag.Subject
+ sJSON.Filename = rng.Filename
+ sJSON.Start.Line = rng.Start.Line
+ sJSON.Start.Column = rng.Start.Column
+ sJSON.Start.Byte = rng.Start.Byte
+ sJSON.End.Line = rng.End.Line
+ sJSON.End.Column = rng.End.Column
+ sJSON.End.Byte = rng.End.Byte
+ }
+
+ diagsJSON = append(diagsJSON, diagJSON)
+ }
+
+ src, err := json.MarshalIndent(DiagnosticsJSON{diagsJSON}, "", " ")
+ if err != nil {
+ return err
+ }
+ _, err = wr.w.Write(src)
+ wr.w.Write([]byte{'\n'})
+ return err
+}
+
+type flusher interface {
+ Flush() error
+}
+
+func flush(maybeFlusher interface{}) error {
+ if f, ok := maybeFlusher.(flusher); ok {
+ return f.Flush()
+ }
+ return nil
+}
diff --git a/cmd/hcldec/main.go b/cmd/hcldec/main.go
index dde87a5..4f14226 100644
--- a/cmd/hcldec/main.go
+++ b/cmd/hcldec/main.go
@@ -26,6 +26,7 @@ var vars = &varSpecs{}
var (
specFile = flag.StringP("spec", "s", "", "path to spec file (required)")
outputFile = flag.StringP("out", "o", "", "write to the given file, instead of stdout")
+ diagsFormat = flag.StringP("diags", "", "", "format any returned diagnostics in the given format; currently only \"json\" is accepted")
showVarRefs = flag.BoolP("var-refs", "", false, "rather than decoding input, produce a JSON description of the variables referenced by it")
showVersion = flag.BoolP("version", "v", false, "show the version number and immediately exit")
)
@@ -35,13 +36,6 @@ var diagWr hcl.DiagnosticWriter // initialized in init
func init() {
flag.VarP(vars, "vars", "V", "provide variables to the given configuration file(s)")
-
- color := terminal.IsTerminal(int(os.Stderr.Fd()))
- w, _, err := terminal.GetSize(int(os.Stdout.Fd()))
- if err != nil {
- w = 80
- }
- diagWr = hcl.NewDiagnosticTextWriter(os.Stderr, parser.Files(), uint(w), color)
}
func main() {
@@ -55,6 +49,21 @@ func main() {
args := flag.Args()
+ switch *diagsFormat {
+ case "":
+ color := terminal.IsTerminal(int(os.Stderr.Fd()))
+ w, _, err := terminal.GetSize(int(os.Stdout.Fd()))
+ if err != nil {
+ w = 80
+ }
+ diagWr = hcl.NewDiagnosticTextWriter(os.Stderr, parser.Files(), uint(w), color)
+ case "json":
+ diagWr = &jsonDiagWriter{w: os.Stderr}
+ default:
+ fmt.Fprintf(os.Stderr, "Invalid diagnostics format %q: only \"json\" is supported.\n", *diagsFormat)
+ os.Exit(2)
+ }
+
err := realmain(args)
if err != nil {
@@ -75,6 +84,7 @@ func realmain(args []string) error {
diags = append(diags, specDiags...)
if specDiags.HasErrors() {
diagWr.WriteDiagnostics(diags)
+ flush(diagWr)
os.Exit(2)
}
@@ -146,6 +156,7 @@ func realmain(args []string) error {
if diags.HasErrors() {
diagWr.WriteDiagnostics(diags)
+ flush(diagWr)
os.Exit(2)
}
@@ -170,6 +181,7 @@ func realmain(args []string) error {
if diags.HasErrors() {
diagWr.WriteDiagnostics(diags)
+ flush(diagWr)
os.Exit(2)
}