From f69eb54b82669a2e5d020a50fb349234c0ad7982 Mon Sep 17 00:00:00 2001 From: Leo Correa Date: Fri, 3 Apr 2015 12:49:11 -0400 Subject: [PATCH] Pretty print JSON config. Also, print line number highlight the entire JSON line when a syntax error occurs --- config/config.go | 48 +++++++++++++++++++++++++++++++++++-------- config/config_test.go | 2 +- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/config/config.go b/config/config.go index 569f48e2c..68c1c7c09 100644 --- a/config/config.go +++ b/config/config.go @@ -1,6 +1,8 @@ package config import ( + "bufio" + "bytes" "encoding/json" "errors" "fmt" @@ -105,8 +107,16 @@ func (c *Config) Write() error { } defer f.Close() - e := json.NewEncoder(f) - return e.Encode(c) + b, err := json.MarshalIndent(c, "", "\t") + if err != nil { + return err + } + + if _, err := f.Write(b); err != nil { + return err + } + + return nil } func (c *Config) load(argPath string) error { @@ -149,8 +159,9 @@ func (c *Config) read() error { if _, serr := f.Seek(0, os.SEEK_SET); serr != nil { log.Fatalf("seek error: %v", serr) } - extra = fmt.Sprintf(":\nThe file contains invalid JSON syntax at '%s' <~", - findInvalidJSON(f, serr.Offset)) + line, str := findInvalidJSON(f, serr.Offset) + extra = fmt.Sprintf(":\ninvalid JSON syntax at line %d:\n%s", + line, str) } return fmt.Errorf("error parsing JSON in the config file %s%s\n%s", f.Name(), extra, err) } @@ -158,13 +169,32 @@ func (c *Config) read() error { return nil } -func findInvalidJSON(f io.ReaderAt, pos int64) string { - buf := make([]byte, 13) - if _, err := f.ReadAt(buf, pos-13); err != nil { - log.Fatalf("read error: %v", err) +func findInvalidJSON(f io.Reader, pos int64) (int, string) { + var ( + col int + line int + errLine []byte + ) + buf := new(bytes.Buffer) + fb := bufio.NewReader(f) + + for c := int64(0); c < pos; { + b, err := fb.ReadBytes('\n') + if err != nil { + log.Fatalf("read error: %v", err) + } + c += int64(len(b)) + col = len(b) - int(c-pos) + + line++ + errLine = b + } + + if len(errLine) != 0 { + buf.WriteString(fmt.Sprintf("%5d: %s <~", line, errLine[:col])) } - return string(buf) + return line, buf.String() } // IsAuthenticated returns true if the config contains an API key. diff --git a/config/config_test.go b/config/config_test.go index 59f5cd89a..4978bfac2 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -123,7 +123,7 @@ func TestLoad_InvalidJSON(t *testing.T) { err = c.load("~/config_invalid.json") if assert.Error(t, err) { - assert.Contains(t, err.Error(), "The file contains invalid JSON syntax") + assert.Contains(t, err.Error(), "invalid JSON syntax") } }