Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it possible to process yaml instead of json #74

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
module github.com/tomnomnom/gron

go 1.14

require (
github.com/fatih/color v1.7.0
github.com/go-yaml/yaml v2.1.0+incompatible
github.com/mattn/go-colorable v0.0.9
github.com/mattn/go-isatty v0.0.4 // indirect
github.com/nwidger/jsoncolor v0.0.0-20170215171346-75a6de4340e5
github.com/pkg/errors v0.8.0
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect
)
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
Expand All @@ -8,3 +10,9 @@ github.com/nwidger/jsoncolor v0.0.0-20170215171346-75a6de4340e5 h1:d+C3xJdxZT7wN
github.com/nwidger/jsoncolor v0.0.0-20170215171346-75a6de4340e5/go.mod h1:GYFm0zZgTNeoK1QxuIofRDasy2ibmaJZhZLzwsMXUF4=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a h1:i47hUS795cOydZI4AwJQCKXOr4BvxzvikwDoDtHhP2Y=
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
36 changes: 32 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"sort"

"github.com/fatih/color"
"github.com/go-yaml/yaml"
"github.com/mattn/go-colorable"
"github.com/nwidger/jsoncolor"
"github.com/pkg/errors"
Expand All @@ -32,6 +33,7 @@ const (
optMonochrome = 1 << iota
optNoSort
optJSON
optYAML
)

// Output colors
Expand All @@ -49,7 +51,7 @@ var gronVersion = "dev"

func init() {
flag.Usage = func() {
h := "Transform JSON (from a file, URL, or stdin) into discrete assignments to make it greppable\n\n"
h := "Transform JSON or YAML (from a file, URL, or stdin) into discrete assignments to make it greppable\n\n"

h += "Usage:\n"
h += " gron [OPTIONS] [FILE|URL|-]\n\n"
Expand All @@ -61,6 +63,7 @@ func init() {
h += " -s, --stream Treat each line of input as a separate JSON object\n"
h += " -k, --insecure Disable certificate validation\n"
h += " -j, --json Represent gron data as JSON stream\n"
h += " -y, --yaml Treat input as YAML instead of JSON\n"
h += " --no-sort Don't sort output (faster)\n"
h += " --version Print version information\n\n"

Expand Down Expand Up @@ -94,6 +97,7 @@ func main() {
versionFlag bool
insecureFlag bool
jsonFlag bool
yamlFlag bool
)

flag.BoolVar(&ungronFlag, "ungron", false, "")
Expand All @@ -110,6 +114,8 @@ func main() {
flag.BoolVar(&insecureFlag, "insecure", false, "")
flag.BoolVar(&jsonFlag, "j", false, "")
flag.BoolVar(&jsonFlag, "json", false, "")
flag.BoolVar(&yamlFlag, "y", false, "")
flag.BoolVar(&yamlFlag, "yaml", false, "")

flag.Parse()

Expand Down Expand Up @@ -154,6 +160,9 @@ func main() {
if jsonFlag {
opts = opts | optJSON
}
if yamlFlag {
opts = opts | optYAML
}

// Pick the appropriate action: gron, ungron or gronStream
var a actionFn = gron
Expand All @@ -176,6 +185,20 @@ func main() {
// code and any error that occurred
type actionFn func(io.Reader, io.Writer, int) (int, error)

type decoder interface {
Decode(interface{}) error
}

func makeDecoder(r io.Reader, opts int) decoder {
if opts&optYAML > 0 {
return yaml.NewDecoder(r)
} else {
d := json.NewDecoder(r)
d.UseNumber()
return d
}
}

// gron is the default action. Given JSON as the input it returns a list
// of assignment statements. Possible options are optNoSort and optMonochrome
func gron(r io.Reader, w io.Writer, opts int) (int, error) {
Expand All @@ -188,7 +211,12 @@ func gron(r io.Reader, w io.Writer, opts int) (int, error) {
conv = statementToColorString
}

ss, err := statementsFromJSON(r, statement{{"json", typBare}})
top := "json"
if opts&optYAML > 0 {
top = "yaml"
}

ss, err := statementsFromJSON(makeDecoder(r, opts), statement{{top, typBare}})
if err != nil {
goto out
}
Expand Down Expand Up @@ -268,10 +296,10 @@ func gronStream(r io.Reader, w io.Writer, opts int) (int, error) {
i = 0
for sc.Scan() {

line := bytes.NewBuffer(sc.Bytes())
d := makeDecoder(bytes.NewBuffer(sc.Bytes()), opts)

var ss statements
ss, err = statementsFromJSON(line, makePrefix(i))
ss, err = statementsFromJSON(d, makePrefix(i))
i++
if err != nil {
goto out
Expand Down
17 changes: 12 additions & 5 deletions statements.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package main
import (
"encoding/json"
"fmt"
"io"
"reflect"
"strconv"
"strings"
Expand Down Expand Up @@ -385,11 +384,9 @@ func (ss statements) Contains(search statement) bool {

// statementsFromJSON takes an io.Reader containing JSON
// and returns statements or an error on failure
func statementsFromJSON(r io.Reader, prefix statement) (statements, error) {
func statementsFromJSON(r decoder, prefix statement) (statements, error) {
var top interface{}
d := json.NewDecoder(r)
d.UseNumber()
err := d.Decode(&top)
err := r.Decode(&top)
if err != nil {
return nil, err
}
Expand All @@ -408,6 +405,16 @@ func (ss *statements) fill(prefix statement, v interface{}) {
// Recurse into objects and arrays
switch vv := v.(type) {

case map[interface{}]interface{}:
// It's an object
for k, sub := range vv {
ks := fmt.Sprintf("%v", k)
if validIdentifier(ks) {
ss.fill(prefix.withBare(ks), sub)
} else {
ss.fill(prefix.withQuotedKey(ks), sub)
}
}
case map[string]interface{}:
// It's an object
for k, sub := range vv {
Expand Down
52 changes: 51 additions & 1 deletion statements_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func TestStatementsSimple(t *testing.T) {
"": 2
}`)

ss, err := statementsFromJSON(bytes.NewReader(j), statement{{"json", typBare}})
ss, err := statementsFromJSON(makeDecoder(bytes.NewReader(j), 0), statement{{"json", typBare}})

if err != nil {
t.Errorf("Want nil error from makeStatementsFromJSON() but got %s", err)
Expand Down Expand Up @@ -65,6 +65,56 @@ func TestStatementsSimple(t *testing.T) {

}

func TestStatementsSimpleYaml(t *testing.T) {

j := []byte(`'': 2
a quoted: value
anarr:
- 1
- 1.5
anob:
foo: bar
anull:
bool1: true
bool2: false
dotted: A dotted value
else: 1
x: |
y: "z"
id: 66912849`)

ss, err := statementsFromJSON(makeDecoder(bytes.NewReader(j), optYAML), statement{{"yaml", typBare}})

if err != nil {
t.Errorf("Want nil error from makeStatementsFromJSON() but got %s", err)
}

wants := statementsFromStringSlice([]string{
`yaml = {};`,
`yaml.dotted = "A dotted value";`,
`yaml["a quoted"] = "value";`,
`yaml.bool1 = true;`,
`yaml.bool2 = false;`,
`yaml.anull = null;`,
`yaml.anarr = [];`,
`yaml.anarr[0] = 1;`,
`yaml.anarr[1] = 1.5;`,
`yaml.anob = {};`,
`yaml.anob.foo = "bar";`,
`yaml["else"] = 1;`,
`yaml.id = 66912849;`,
`yaml[""] = 2;`,
`yaml.x = "y: \"z\"\n";`,
})

t.Logf("Have: %#v", ss)
for _, want := range wants {
if !ss.Contains(want) {
t.Errorf("Statement group should contain `%s` but doesn't", want)
}
}

}
func TestStatementsSorting(t *testing.T) {
want := statementsFromStringSlice([]string{
`json.a = true;`,
Expand Down
4 changes: 4 additions & 0 deletions token.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,14 @@ func (t token) formatColor() string {
func valueTokenFromInterface(v interface{}) token {
switch vv := v.(type) {

case map[interface{}]interface{}:
return token{"{}", typEmptyObject}
case map[string]interface{}:
return token{"{}", typEmptyObject}
case []interface{}:
return token{"[]", typEmptyArray}
case int, float64:
return token{fmt.Sprintf("%v", vv), typNumber}
case json.Number:
return token{vv.String(), typNumber}
case string:
Expand Down