From 257c8457cc6d979256fd17c05ae0358fd036aaa5 Mon Sep 17 00:00:00 2001 From: hperl <34397+hperl@users.noreply.github.com> Date: Mon, 5 Dec 2022 12:52:15 +0100 Subject: [PATCH] feat: add --output=jsonpointer --- cmdx/printing.go | 52 +++++++++++++++++++++++++++++++++++-------- cmdx/printing_test.go | 18 ++++++++++++++- go.mod | 1 + 3 files changed, 61 insertions(+), 10 deletions(-) diff --git a/cmdx/printing.go b/cmdx/printing.go index ae3bf467..37f7e8c2 100644 --- a/cmdx/printing.go +++ b/cmdx/printing.go @@ -12,6 +12,7 @@ import ( "strings" "text/tabwriter" + "github.com/go-openapi/jsonpointer" "github.com/goccy/go-yaml" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -39,13 +40,14 @@ type ( ) const ( - FormatQuiet format = "quiet" - FormatTable format = "table" - FormatJSON format = "json" - FormatJSONPath format = "jsonpath" - FormatJSONPretty format = "json-pretty" - FormatYAML format = "yaml" - FormatDefault format = "default" + FormatQuiet format = "quiet" + FormatTable format = "table" + FormatJSON format = "json" + FormatJSONPath format = "jsonpath" + FormatJSONPointer format = "jsonpointer" + FormatJSONPretty format = "json-pretty" + FormatYAML format = "yaml" + FormatDefault format = "default" FlagFormat = "format" @@ -84,6 +86,8 @@ func PrintRow(cmd *cobra.Command, row TableRow) { printJSON(cmd.OutOrStdout(), row.Interface(), true, "") case FormatJSONPath: printJSON(cmd.OutOrStdout(), row.Interface(), true, getPath(cmd)) + case FormatJSONPointer: + printJSON(cmd.OutOrStdout(), filterJSONPointer(cmd, row.Interface()), true, "") case FormatTable, FormatDefault: w := tabwriter.NewWriter(cmd.OutOrStdout(), 0, 8, 1, '\t', 0) @@ -96,6 +100,26 @@ func PrintRow(cmd *cobra.Command, row TableRow) { } } +func filterJSONPointer(cmd *cobra.Command, data any) any { + f, err := cmd.Flags().GetString(FlagFormat) + // unexpected error + Must(err, "flag access error: %s", err) + _, jsonptr, found := strings.Cut(f, "=") + if !found { + _, _ = fmt.Fprintf(os.Stderr, + "Format %s is missing a JSON pointer, e.g., --%s=%s=. The path syntax is described at https://datatracker.ietf.org/doc/html/draft-ietf-appsawg-json-pointer-07.", + f, FlagFormat, f) + os.Exit(1) + } + ptr, err := jsonpointer.New(jsonptr) + Must(err, "invalid JSON pointer: %s", err) + + result, _, err := ptr.Get(data) + Must(err, "failed to apply JSON pointer: %s", err) + + return result +} + func PrintTable(cmd *cobra.Command, table Table) { f := getFormat(cmd) @@ -121,6 +145,8 @@ func PrintTable(cmd *cobra.Command, table Table) { printJSON(cmd.OutOrStdout(), table.Interface(), true, "") case FormatJSONPath: printJSON(cmd.OutOrStdout(), table.Interface(), true, getPath(cmd)) + case FormatJSONPointer: + printJSON(cmd.OutOrStdout(), filterJSONPointer(cmd, table.Interface()), true, "") case FormatYAML: printYAML(cmd.OutOrStdout(), table.Interface()) default: @@ -164,6 +190,12 @@ func PrintJSONAble(cmd *cobra.Command, d interface{ String() string }) { v = i } printJSON(cmd.OutOrStdout(), v, true, path) + case FormatJSONPointer: + var v interface{} = d + if i, ok := d.(interfacer); ok { + v = i + } + printJSON(cmd.OutOrStdout(), filterJSONPointer(cmd, v), true, "") case FormatYAML: var v interface{} = d if i, ok := d.(interfacer); ok { @@ -200,6 +232,8 @@ func getFormat(cmd *cobra.Command) format { return FormatJSON case strings.HasPrefix(f, string(FormatJSONPath)): return FormatJSONPath + case strings.HasPrefix(f, string(FormatJSONPointer)): + return FormatJSONPointer case f == string(FormatJSONPretty): return FormatJSONPretty case f == string(FormatYAML): @@ -249,12 +283,12 @@ func printYAML(w io.Writer, v interface{}) { } func RegisterJSONFormatFlags(flags *pflag.FlagSet) { - flags.String(FlagFormat, string(FormatDefault), fmt.Sprintf("Set the output format. One of %s, %s, %s, %s and %s.", FormatDefault, FormatJSON, FormatYAML, FormatJSONPretty, FormatJSONPath)) + flags.String(FlagFormat, string(FormatDefault), fmt.Sprintf("Set the output format. One of %s, %s, %s, %s, %s and %s.", FormatDefault, FormatJSON, FormatYAML, FormatJSONPretty, FormatJSONPath, FormatJSONPointer)) } func RegisterFormatFlags(flags *pflag.FlagSet) { RegisterNoiseFlags(flags) - flags.String(FlagFormat, string(FormatDefault), fmt.Sprintf("Set the output format. One of %s, %s, %s, %s, and %s.", FormatTable, FormatJSON, FormatYAML, FormatJSONPretty, FormatJSONPath)) + flags.String(FlagFormat, string(FormatDefault), fmt.Sprintf("Set the output format. One of %s, %s, %s, %s, %s and %s.", FormatTable, FormatJSON, FormatYAML, FormatJSONPretty, FormatJSONPath, FormatJSONPointer)) } type bodyer interface { diff --git a/cmdx/printing_test.go b/cmdx/printing_test.go index d6296d00..52422dd9 100644 --- a/cmdx/printing_test.go +++ b/cmdx/printing_test.go @@ -128,6 +128,18 @@ func TestPrinting(t *testing.T) { fArgs: []string{"--" + FlagFormat, string(FormatJSONPretty)}, contained: tr, }, + { + fArgs: []string{"--" + FlagFormat, string(FormatJSONPointer) + "=/0"}, + contained: []string{"AAA"}, + }, + { + fArgs: []string{"--" + FlagFormat, string(FormatJSONPointer) + "=/2"}, + contained: []string{"CCC"}, + }, + { + fArgs: []string{"--" + FlagFormat, string(FormatJSONPointer) + "=/1"}, + contained: []string{"BBB"}, + }, { fArgs: []string{"--" + FlagFormat, string(FormatJSONPath) + "=0"}, contained: []string{"AAA"}, @@ -224,6 +236,10 @@ func TestPrinting(t *testing.T) { fArgs: []string{"--" + FlagFormat, string(FormatJSONPath) + "=1.1"}, contained: []string{tb.t[1][1]}, }, + { + fArgs: []string{"--" + FlagFormat, string(FormatJSONPointer) + "=/1/1"}, + contained: []string{tb.t[1][1]}, + }, { fArgs: []string{"--" + FlagFormat, string(FormatYAML)}, contained: append(tb.t[0], tb.t[1]...), @@ -330,7 +346,7 @@ func TestPrinting(t *testing.T) { t.Run("method=jsonable", func(t *testing.T) { t.Run("case=nil", func(t *testing.T) { - for _, f := range []format{FormatDefault, FormatJSON, FormatJSONPretty, FormatJSONPath, FormatYAML} { + for _, f := range []format{FormatDefault, FormatJSON, FormatJSONPretty, FormatJSONPath, FormatJSONPointer, FormatYAML} { t.Run("format="+string(f), func(t *testing.T) { out := &bytes.Buffer{} cmd := &cobra.Command{} diff --git a/go.mod b/go.mod index 9a1cf86e..da7066bb 100644 --- a/go.mod +++ b/go.mod @@ -133,6 +133,7 @@ require ( github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/errors v0.20.3 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/strfmt v0.21.3 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/go-pdf/fpdf v0.6.0 // indirect