From f1049b37c1ed6052a11fbcc395e77c672fa8d2e9 Mon Sep 17 00:00:00 2001 From: Dain Cilke Date: Sat, 29 Apr 2023 23:14:57 +0200 Subject: [PATCH] feat: add default config --- README.md | 39 ++++++++++++++++ go.mod | 4 +- go.sum | 3 +- integration/cli_test.go | 14 +++--- .../testdata/golden/TestCLI_Help/--help | 8 ++-- integration/testdata/golden/TestCLI_Help/-h | 8 ++-- main.go | 45 ++++++++++++++++--- 7 files changed, 96 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 886675f..9dfecd8 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,45 @@ brew tap dcilke/taps brew install dcilke/taps/hz ``` +## Help + +```zsh +hz --help +Usage: + hz [FILE] + +Application Options: + -l, --level= only output lines at this level + -s, --strict exclude non JSON output + -f, --flat flatten objects + -v, --vertical vertical output + -r, --raw raw output + -n, --no-pin exclude pinning of fields + +Help Options: + -h, --help Show this help message +``` + +## Config + +Default command options can be specified in a config file located at `$HOME/.config/hz/config.yml`. + +```yaml +level: + - trace + - debug + - info + - warn + - error + - fatal + - panic +strict: false +flat: false +vertical: false +plain: false +noPin: false +``` + ## Why? I use [zerolog](https://github.com/rs/zerolog) for structured logging and want to be able to quickly tap into the log streams. diff --git a/go.mod b/go.mod index 6bed956..13ab702 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/jessevdk/go-flags v1.5.0 github.com/mattn/go-colorable v0.1.12 github.com/stretchr/testify v1.8.2 + gopkg.in/yaml.v3 v3.0.1 ) require ( @@ -14,7 +15,6 @@ require ( github.com/dcilke/goj v0.0.4 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sanity-io/litter v1.5.5 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( @@ -22,5 +22,5 @@ require ( github.com/dcilke/heron v0.2.0 github.com/kylelemons/godebug v1.1.0 // indirect github.com/mattn/go-isatty v0.0.14 // indirect - golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect + golang.org/x/sys v0.6.0 // indirect ) diff --git a/go.sum b/go.sum index 10d9cb4..f3fcacb 100644 --- a/go.sum +++ b/go.sum @@ -33,8 +33,9 @@ github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/integration/cli_test.go b/integration/cli_test.go index f8f477d..9e1c03b 100644 --- a/integration/cli_test.go +++ b/integration/cli_test.go @@ -12,7 +12,7 @@ var filecases = []string{"strings", "ndjson", "pretty", "array", "mixed"} func TestCLI(t *testing.T) { for _, tc := range filecases { t.Run(tc, func(t *testing.T) { - output, err := hz(fn(tc), "--plain") + output, err := hz(fn(tc), "--raw") require.NoError(t, err) golden.Assert(t, output) }) @@ -22,7 +22,7 @@ func TestCLI(t *testing.T) { func TestCLI_Strict(t *testing.T) { for _, tc := range filecases { t.Run(tc, func(t *testing.T) { - output, err := hz(fn(tc), "--plain", "--strict") + output, err := hz(fn(tc), "--raw", "--strict") require.NoError(t, err) golden.Assert(t, output) }) @@ -33,7 +33,7 @@ func TestCLI_Level(t *testing.T) { for _, tc := range filecases { for _, level := range []string{"trace", "debug", "info", "warn", "error", "fatal", "panic"} { t.Run(tc+"/"+level, func(t *testing.T) { - output, err := hz(fn(tc), "--plain", "--level", level) + output, err := hz(fn(tc), "--raw", "--level", level) require.NoError(t, err) golden.Assert(t, output) }) @@ -45,7 +45,7 @@ func TestCLI_Help(t *testing.T) { testcases := []string{"--help", "-h"} for _, tc := range testcases { t.Run(tc, func(t *testing.T) { - output, err := hz(tc, "--plain") + output, err := hz(tc, "--raw") require.NoError(t, err) golden.Assert(t, output) }) @@ -53,13 +53,13 @@ func TestCLI_Help(t *testing.T) { } func TestCLI_Flat(t *testing.T) { - output, err := hz(fn("nested"), "--plain", "--flat") + output, err := hz(fn("nested"), "--raw", "--flat") require.NoError(t, err) golden.Assert(t, output) } func TestCLI_Vert(t *testing.T) { - output, err := hz(fn("nested"), "--plain", "--vertical") + output, err := hz(fn("nested"), "--raw", "--vertical") require.NoError(t, err) golden.Assert(t, output) } @@ -71,7 +71,7 @@ func TestCLI_Color(t *testing.T) { } func TestCLI_NoPin(t *testing.T) { - output, err := hz(fn("mixed"), "--plain", "--no-pin") + output, err := hz(fn("mixed"), "--raw", "--no-pin") require.NoError(t, err) golden.Assert(t, output) } diff --git a/integration/testdata/golden/TestCLI_Help/--help b/integration/testdata/golden/TestCLI_Help/--help index c0eecd2..5035e9e 100644 --- a/integration/testdata/golden/TestCLI_Help/--help +++ b/integration/testdata/golden/TestCLI_Help/--help @@ -3,11 +3,11 @@ Usage: Application Options: -l, --level= only output lines at this level - -s, --strict strict mode - -f, --flat flatten output + -s, --strict exclude non JSON output + -f, --flat flatten objects -v, --vertical vertical output - -p, --plain plain (no color) output - -n, --no-pin don't pin any fields + -r, --raw raw output + -n, --no-pin exclude pinning of fields Help Options: -h, --help Show this help message diff --git a/integration/testdata/golden/TestCLI_Help/-h b/integration/testdata/golden/TestCLI_Help/-h index c0eecd2..5035e9e 100644 --- a/integration/testdata/golden/TestCLI_Help/-h +++ b/integration/testdata/golden/TestCLI_Help/-h @@ -3,11 +3,11 @@ Usage: Application Options: -l, --level= only output lines at this level - -s, --strict strict mode - -f, --flat flatten output + -s, --strict exclude non JSON output + -f, --flat flatten objects -v, --vertical vertical output - -p, --plain plain (no color) output - -n, --no-pin don't pin any fields + -r, --raw raw output + -n, --no-pin exclude pinning of fields Help Options: -h, --help Show this help message diff --git a/main.go b/main.go index 08e3b31..475a55a 100644 --- a/main.go +++ b/main.go @@ -3,28 +3,47 @@ package main import ( "fmt" "os" + "path/filepath" "github.com/dcilke/gu" "github.com/dcilke/heron" "github.com/dcilke/hz/pkg/writer" "github.com/jessevdk/go-flags" + "gopkg.in/yaml.v3" ) const ( newline = "\n" ) +var cfgPath string + +func init() { + home, err := os.UserHomeDir() + if err != nil { + home = "." // fallback to current directory + } + cfgPath = filepath.Join(home, ".config", "hz", "config.yml") +} + type Cmd struct { - Level []string `short:"l" long:"level" description:"only output lines at this level"` - Strict bool `short:"s" long:"strict" description:"strict mode"` - Flat bool `short:"f" long:"flat" description:"flatten output"` - Vertical bool `short:"v" long:"vertical" description:"vertical output"` - Plain bool `short:"p" long:"plain" description:"plain (no color) output"` - NoPin bool `short:"n" long:"no-pin" description:"don't pin any fields"` + Level []string `short:"l" long:"level" description:"only output lines at this level" yaml:"level"` + Strict bool `short:"s" long:"strict" description:"exclude non JSON output" yaml:"strict"` + Flat bool `short:"f" long:"flat" description:"flatten objects" yaml:"flat"` + Vertical bool `short:"v" long:"vertical" description:"vertical output" yaml:"vertical"` + Raw bool `short:"r" long:"raw" description:"raw output" yaml:"plain"` + NoPin bool `short:"n" long:"no-pin" description:"exclude pinning of fields" yaml:"noPin"` } func main() { var cmd Cmd + // read in config file, if it exists + err := loadDefaults(&cmd) + if err != nil { + fmt.Fprint(os.Stderr, fmt.Errorf("WARN: unable to load config: %w", err), "\n") + } + + // parse command line flags parser := flags.NewParser(&cmd, flags.HelpFlag|flags.PassDoubleDash) parser.Usage = "[FILE]" filenames, err := parser.Parse() @@ -45,7 +64,7 @@ func main() { writer.WithLevelFilters(cmd.Level), writer.WithFlatten(cmd.Flat), writer.WithVertical(cmd.Vertical), - writer.WithColor(!cmd.Plain), + writer.WithColor(!cmd.Raw), } if cmd.NoPin { @@ -97,3 +116,15 @@ func main() { h.Process(os.Stdin) } + +func loadDefaults(cfg *Cmd) error { + if _, err := os.Stat(cfgPath); os.IsNotExist(err) { + return nil + } + + bytes, err := os.ReadFile(cfgPath) + if err != nil { + return fmt.Errorf("unable to read config file: %w", err) + } + return yaml.Unmarshal(bytes, &cfg) +}