Skip to content

Commit

Permalink
Merge pull request #2 from lafrenierejm/cobra
Browse files Browse the repository at this point in the history
Rewrite CLI entrypoint using Cobra
  • Loading branch information
lafrenierejm authored Jul 22, 2023
2 parents 1d09c6c + 396c8ed commit 041711a
Show file tree
Hide file tree
Showing 19 changed files with 359 additions and 620 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
uses: cachix/install-nix-action@v21

- name: Ensure the build succeeds
run: nix build
run: nix build --print-build-logs

- name: Run `nix flake check` to run formatters, linters, and tests
run: nix flake check --print-build-logs
219 changes: 219 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/*
Copyright © 2016 Tom Hudson <mail@tomnomnom.com>
Copyright © 2023 Joseph LaFreniere <git@lafreniere.xyz>
*/
package cmd

import (
"bufio"
"encoding/json"
"fmt"
"io"
"log"
"os"

internal "github.com/lafrenierejm/gron/internal/gron"
"github.com/mattn/go-colorable"
"github.com/spf13/cobra"
)

var version = "0.0.1"

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "gron",
Version: version,
Short: "Transform JSON or YAML into discrete assignments to make it greppable",
Long: `gron transforms JSON or YAML (from a file, URL, or stdin) into discrete assignments to make it easier to grep for what you want and see the absolute "path" to it.
Examples:
gron /tmp/apiresponse.json
gron http://jsonplaceholder.typicode.com/users/1
curl -s http://jsonplaceholder.typicode.com/users/1 | gron
gron http://jsonplaceholder.typicode.com/users/1 | grep company | gron --ungron
`,
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
colorizeFlag, err := cmd.Flags().GetBool("colorize")
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
insecureFlag, err := cmd.Flags().GetBool("insecure")
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
jsonFlag, err := cmd.Flags().GetBool("json")
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
monochromeFlag, err := cmd.Flags().GetBool("monochrome")
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
sortFlag, err := cmd.Flags().GetBool("sort")
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
streamFlag, err := cmd.Flags().GetBool("stream")
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
ungronFlag, err := cmd.Flags().GetBool("ungron")
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
valuesFlag, err := cmd.Flags().GetBool("values")
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
yamlFlag, err := cmd.Flags().GetBool("yaml")
if err != nil {
fmt.Println(err)
os.Exit(-1)
}

var rawInput io.Reader
if len(args) == 0 || args[0] == "" || args[0] == "-" {
rawInput = os.Stdin
} else {
filename := args[0]
if validURL(filename) {
rawInput, err = getURL(filename, insecureFlag)
if err != nil {
log.Println(err)
os.Exit(1)
}
} else {
rawInput, err = os.Open(filename)
if err != nil {
log.Println(err)
os.Exit(1)
}
}
}

var conv internal.StatementConv = internal.StatementToString
var colorize bool = false
if colorizeFlag {
colorize = true
} else if !monochromeFlag {
nocolorEnv, nocolorEnvPresent := os.LookupEnv("NO_COLOR")
if nocolorEnvPresent && nocolorEnv != "" {
colorize = false
} else {
colorize = true
}
}
if colorize {
conv = internal.StatementToColorString
}

var actionExit int
var actionErr error
if ungronFlag {
actionExit, actionErr = internal.Ungron(
rawInput,
colorable.NewColorableStdout(),
jsonFlag,
colorize,
)
} else if valuesFlag {
actionExit, actionErr = gronValues(rawInput, colorable.NewColorableStdout())
} else if streamFlag {
actionExit, actionErr = internal.GronStream(
rawInput,
colorable.NewColorableStdout(),
conv,
yamlFlag,
sortFlag,
jsonFlag,
)
} else {
actionExit, actionErr = internal.Gron(
rawInput,
colorable.NewColorableStdout(),
conv,
yamlFlag,
sortFlag,
jsonFlag,
)
}

if actionExit != 0 || actionErr != nil {
log.Println(err)
}
os.Exit(actionExit)
},
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}

func init() {
rootCmd.Flags().BoolP("colorize", "c", false, "Colorize output (default on TTY)")
rootCmd.Flags().BoolP("insecure", "k", false, "Disable certificate validation when reading from a URL")
rootCmd.Flags().BoolP("json", "j", false, "Represent gron data as JSON stream")
rootCmd.Flags().BoolP("monochrome", "m", false, "Do not colorize output")
rootCmd.Flags().BoolP("sort", "", true, "Sort output")
rootCmd.Flags().BoolP("stream", "s", false, "Treat each line of input as a separate JSON object")
rootCmd.Flags().BoolP("ungron", "u", false, "Reverse the operation (turn assignments back into JSON)")
rootCmd.Flags().BoolP("values", "v", false, "Print just the values of provided assignments")
rootCmd.Flags().BoolP("version", "", false, "Print version information")
rootCmd.Flags().BoolP("yaml", "y", false, "Treat input as YAML instead of JSON")
}

// gronValues prints just the scalar values from some input gron statements
// without any quotes or anything of that sort; a bit like jq -r
// e.g. json[0].user.name = "Sam"; -> Sam
func gronValues(r io.Reader, w io.Writer) (int, error) {
scanner := bufio.NewScanner(os.Stdin)

for scanner.Scan() {
s := internal.StatementFromString(scanner.Text())

// strip off the leading 'json' bare key
if s[0].Typ == internal.TypBare && s[0].Text == "json" {
s = s[1:]
}

// strip off the leading dots
if s[0].Typ == internal.TypDot || s[0].Typ == internal.TypLBrace {
s = s[1:]
}

for _, t := range s {
switch t.Typ {
case internal.TypString:
var text string
err := json.Unmarshal([]byte(t.Text), &text)
if err != nil {
// just swallow errors and try to continue
continue
}
fmt.Println(text)

case internal.TypNumber, internal.TypTrue, internal.TypFalse, internal.TypNull:
fmt.Println(t.Text)

default:
// Nothing
}
}
}

return 0, nil
}
5 changes: 2 additions & 3 deletions url.go → cmd/url.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package cmd

import (
"bufio"
Expand Down Expand Up @@ -28,11 +28,10 @@ func getURL(url string, insecure bool) (io.Reader, error) {
if err != nil {
return nil, err
}
req.Header.Set("User-Agent", fmt.Sprintf("gron/%s", gronVersion))
req.Header.Set("User-Agent", fmt.Sprintf("gron/%s", version))
req.Header.Set("Accept", "application/json")

resp, err := client.Do(req)

if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion url_test.go → cmd/url_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package cmd

import (
"testing"
Expand Down
37 changes: 21 additions & 16 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -31,37 +31,40 @@
overlays = [ inputs.gomod2nix.overlays.default (final: prev: { }) ];
config = { };
};
gron = pkgs.buildGoApplication {
pname = "gron";
version = self'.shortRev or "dirty";
# In 'nix develop', we don't need a copy of the source tree
# in the Nix store.
src = ./.;
modules = ./gomod2nix.toml;
meta = with pkgs.lib; {
description =
"Transform JSON or YAML into discrete assignments to make it easier to `grep` for what you want and see the absolute 'path' to it";
homepage = "https://github.com/lafrenierejm/gron";
license = licenses.mit;
maintainers = with maintainers; [ lafrenierejm ];
};
};
in {
# Per-system attributes can be defined here. The self' and inputs'
# module parameters provide easy access to attributes of the same
# system.
packages = rec {
gron = pkgs.buildGoApplication {
pname = "gron";
version = self'.shortRev or "dirty";
# In 'nix develop', we don't need a copy of the source tree
# in the Nix store.
src = ./.;
modules = ./gomod2nix.toml;
meta = with pkgs.lib; {
description =
"Transform JSON or YAML into discrete assignments to make it easier to `grep` for what you want and see the absolute 'path' to it";
homepage = "https://github.com/lafrenierejm/gron";
license = licenses.mit;
maintainers = with maintainers; [ lafrenierejm ];
};
};
inherit gron;
default = gron;
};

apps.default = gron;

# Auto formatters. This also adds a flake check to ensure that the
# source tree was auto formatted.
treefmt.config = {
projectRootFile = ".git/config";
package = pkgs.treefmt;
flakeCheck = false; # use pre-commit's check instead
programs = {
gofmt.enable = true;
gofumpt.enable = true;
prettier.enable = true;
};
settings.formatter = {
Expand Down Expand Up @@ -97,9 +100,11 @@
# Inherit all of the pre-commit hooks.
inputsFrom = [ config.pre-commit.devShell ];
buildInputs = with pkgs; [
cobra-cli
go
go-tools
godef
gofumpt
gomod2nix
gopls
gotools
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ require (
github.com/mattn/go-colorable v0.1.13
github.com/nwidger/jsoncolor v0.3.2
github.com/pkg/errors v0.9.1
github.com/spf13/cobra v1.7.0
gopkg.in/yaml.v3 v3.0.1
)

require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/sys v0.10.0 // indirect
)

Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
Expand All @@ -13,6 +16,11 @@ github.com/nwidger/jsoncolor v0.3.2 h1:rVJJlwAWDJShnbTYOQ5RM7yTA20INyKXlJ/fg4JMh
github.com/nwidger/jsoncolor v0.3.2/go.mod h1:Cs34umxLbJvgBMnVNVqhji9BhoT/N/KinHqZptQ7cf4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
9 changes: 9 additions & 0 deletions gomod2nix.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ schema = 3
[mod."github.com/fatih/color"]
version = "v1.15.0"
hash = "sha256-7b+scFVQeEUoXfeCDd8X2gS8GMoWA+HxjK8wfbypa5s="
[mod."github.com/inconshreveable/mousetrap"]
version = "v1.1.0"
hash = "sha256-XWlYH0c8IcxAwQTnIi6WYqq44nOKUylSWxWO/vi+8pE="
[mod."github.com/mattn/go-colorable"]
version = "v0.1.13"
hash = "sha256-qb3Qbo0CELGRIzvw7NVM1g/aayaz4Tguppk9MD2/OI8="
Expand All @@ -16,6 +19,12 @@ schema = 3
[mod."github.com/pkg/errors"]
version = "v0.9.1"
hash = "sha256-mNfQtcrQmu3sNg/7IwiieKWOgFQOVVe2yXgKBpe/wZw="
[mod."github.com/spf13/cobra"]
version = "v1.7.0"
hash = "sha256-bom9Zpnz8XPwx9IVF+GAodd3NVQ1dM1Uwxn8sy4Gmzs="
[mod."github.com/spf13/pflag"]
version = "v1.0.5"
hash = "sha256-w9LLYzxxP74WHT4ouBspH/iQZXjuAh2WQCHsuvyEjAw="
[mod."golang.org/x/sys"]
version = "v0.10.0"
hash = "sha256-eeifyHj8IcTAruekJAGMCcjFyU2GAIAgvxS36hPZM1U="
Expand Down
Loading

0 comments on commit 041711a

Please sign in to comment.