diff --git a/cmd/base64.go b/cmd/base64.go index 05cf2db..8d19b72 100644 --- a/cmd/base64.go +++ b/cmd/base64.go @@ -47,14 +47,20 @@ Input can be a string argument or piped from stdin.`, if err != nil { return err } - fmt.Print(decoded) - } else { - // Encode to base64 - encoded := base64.Encode(data) - fmt.Print(encoded) + if outputJSON { + return writeJSONValue(cmd.OutOrStdout(), decoded) + } + _, err = fmt.Fprint(cmd.OutOrStdout(), decoded) + return err } - return nil + // Encode to base64 + encoded := base64.Encode(data) + if outputJSON { + return writeJSONValue(cmd.OutOrStdout(), encoded) + } + _, err = fmt.Fprint(cmd.OutOrStdout(), encoded) + return err }, } diff --git a/cmd/count.go b/cmd/count.go index 8b51cd1..a63ea5b 100644 --- a/cmd/count.go +++ b/cmd/count.go @@ -47,6 +47,13 @@ in a formatted table. Input can be a string argument or piped from stdin.`, if err != nil { return err } + if outputJSON { + return writeJSONValue(cmd.OutOrStdout(), map[string]int{ + "characters": stats.Characters, + "spaces": stats.Spaces, + "words": stats.Words, + }) + } _, err = fmt.Fprintln(cmd.OutOrStdout(), table.New().Border(lipgloss.NormalBorder()). Row("Characters", strconv.Itoa(stats.Characters)). diff --git a/cmd/cssfmt.go b/cmd/cssfmt.go index 8648ecc..9f3c57c 100644 --- a/cmd/cssfmt.go +++ b/cmd/cssfmt.go @@ -1,6 +1,7 @@ package cmd import ( + "bytes" "strings" tea "github.com/charmbracelet/bubbletea" @@ -85,10 +86,18 @@ custom spacing. Input can be a string argument or piped from stdin.`, } inputStr := string(data) - err = cssformat.Format(strings.NewReader(inputStr), cmd.OutOrStdout()) + var buffer bytes.Buffer + output := cmd.OutOrStdout() + if outputJSON { + output = &buffer + } + err = cssformat.Format(strings.NewReader(inputStr), output) if err != nil { return cmderror.FormatParseError("cssfmt", inputStr, err) } + if outputJSON { + return writeJSONValue(cmd.OutOrStdout(), buffer.String()) + } return nil }, diff --git a/cmd/cssmin.go b/cmd/cssmin.go index b6ad9fe..52aff8c 100644 --- a/cmd/cssmin.go +++ b/cmd/cssmin.go @@ -1,6 +1,7 @@ package cmd import ( + "bytes" "strings" "github.com/client9/csstool" @@ -41,10 +42,18 @@ Input can be a string argument or piped from stdin.`, } inputStr := string(data) - err = cssformat.Format(strings.NewReader(inputStr), cmd.OutOrStdout()) + var buffer bytes.Buffer + output := cmd.OutOrStdout() + if outputJSON { + output = &buffer + } + err = cssformat.Format(strings.NewReader(inputStr), output) if err != nil { return cmderror.FormatParseError("cssmin", inputStr, err) } + if outputJSON { + return writeJSONValue(cmd.OutOrStdout(), buffer.String()) + } return nil }, diff --git a/cmd/csv2md.go b/cmd/csv2md.go index aa358aa..3cafd7e 100644 --- a/cmd/csv2md.go +++ b/cmd/csv2md.go @@ -2,6 +2,7 @@ package cmd import ( "encoding/csv" + "fmt" "strings" "github.com/skatkov/devtui/internal/cmderror" @@ -53,7 +54,15 @@ and --header to add a main heading (h1) to the output.`, return cmderror.FormatParseError("csv2md", inputStr, err) } - csv2md.Print(csv2md.Convert(csv2mdHeader, records, csv2mdAlignColumns)) + rows := csv2md.Convert(csv2mdHeader, records, csv2mdAlignColumns) + if outputJSON { + return writeJSONValue(cmd.OutOrStdout(), strings.Join(rows, "\n")) + } + for _, row := range rows { + if _, err := fmt.Fprintln(cmd.OutOrStdout(), row); err != nil { + return err + } + } return nil }, diff --git a/cmd/gqlquery.go b/cmd/gqlquery.go index aa67161..b366bc7 100644 --- a/cmd/gqlquery.go +++ b/cmd/gqlquery.go @@ -120,6 +120,9 @@ or piped from stdin.`, f := formatter.NewFormatter(&buf, opts...) f.FormatQueryDocument(query) result := buf.String() + if outputJSON { + return writeJSONValue(cmd.OutOrStdout(), result) + } // Write the result to stdout _, err = fmt.Fprintln(cmd.OutOrStdout(), result) diff --git a/cmd/iban.go b/cmd/iban.go index fa09442..8a5d969 100644 --- a/cmd/iban.go +++ b/cmd/iban.go @@ -40,13 +40,15 @@ Use the --formatted flag to output the IBAN in paper format with spaces.`, } // Format output based on flag + output := generatedIban if ibanFormatted { - fmt.Println(iban.PaperFormat(generatedIban)) - } else { - fmt.Println(generatedIban) + output = iban.PaperFormat(generatedIban) } - - return nil + if outputJSON { + return writeJSONValue(cmd.OutOrStdout(), output) + } + _, err = fmt.Fprintln(cmd.OutOrStdout(), output) + return err }, } diff --git a/cmd/json2toml.go b/cmd/json2toml.go index db4fb04..d74fba9 100644 --- a/cmd/json2toml.go +++ b/cmd/json2toml.go @@ -72,6 +72,9 @@ results in an interactive terminal interface.`, if err != nil { return cmderror.FormatParseError("json2toml", inputStr, err) } + if outputJSON { + return writeJSONValue(cmd.OutOrStdout(), result) + } _, err = fmt.Fprintln(cmd.OutOrStdout(), result) if err != nil { diff --git a/cmd/json2toon.go b/cmd/json2toon.go index 0215696..d23fa18 100644 --- a/cmd/json2toon.go +++ b/cmd/json2toon.go @@ -45,6 +45,9 @@ reduced token usage (typically 30-60% fewer tokens than JSON).`, if err != nil { return cmderror.FormatParseError("json2toon", inputStr, err) } + if outputJSON { + return writeJSONValue(cmd.OutOrStdout(), result) + } _, err = fmt.Fprint(cmd.OutOrStdout(), result) if err != nil { diff --git a/cmd/json2xml.go b/cmd/json2xml.go index 6814218..3e677eb 100644 --- a/cmd/json2xml.go +++ b/cmd/json2xml.go @@ -44,6 +44,9 @@ Input can be a string argument or piped from stdin.`, if err != nil { return cmderror.FormatParseError("json2xml", inputStr, err) } + if outputJSON { + return writeJSONValue(cmd.OutOrStdout(), result) + } _, err = fmt.Fprintln(cmd.OutOrStdout(), result) if err != nil { diff --git a/cmd/json2yaml.go b/cmd/json2yaml.go index 4db6b72..4714019 100644 --- a/cmd/json2yaml.go +++ b/cmd/json2yaml.go @@ -44,6 +44,9 @@ Input can be a string argument or piped from stdin.`, if err != nil { return cmderror.FormatParseError("json2yaml", inputStr, err) } + if outputJSON { + return writeJSONValue(cmd.OutOrStdout(), result) + } _, err = fmt.Fprintln(cmd.OutOrStdout(), result) if err != nil { diff --git a/cmd/json_output.go b/cmd/json_output.go new file mode 100644 index 0000000..2e66f7b --- /dev/null +++ b/cmd/json_output.go @@ -0,0 +1,14 @@ +package cmd + +import ( + "encoding/json" + "io" +) + +var outputJSON bool + +func writeJSONValue(out io.Writer, value any) error { + encoder := json.NewEncoder(out) + encoder.SetEscapeHTML(false) + return encoder.Encode(value) +} diff --git a/cmd/jsonfmt.go b/cmd/jsonfmt.go index 62d5eed..b9badce 100644 --- a/cmd/jsonfmt.go +++ b/cmd/jsonfmt.go @@ -53,7 +53,6 @@ The output is always valid, properly indented JSON.`, // } result := json.FormatJSON(string(data)) - _, err = fmt.Fprintln(cmd.OutOrStdout(), result) if err != nil { return err diff --git a/cmd/numbers.go b/cmd/numbers.go index 54766fb..f3eea0c 100644 --- a/cmd/numbers.go +++ b/cmd/numbers.go @@ -46,7 +46,7 @@ Input can be a number argument or piped from stdin.`, return err } - if numbersJSONOutput { + if outputJSON { bytes, err := json.MarshalIndent(result, "", " ") if err != nil { return err @@ -66,12 +66,10 @@ Input can be a number argument or piped from stdin.`, } var ( - numbersBase int - numbersJSONOutput bool + numbersBase int ) func init() { rootCmd.AddCommand(numbersCmd) numbersCmd.Flags().IntVarP(&numbersBase, "base", "b", 10, "input number base (2, 8, 10, 16)") - numbersCmd.Flags().BoolVar(&numbersJSONOutput, "json", false, "output conversions as JSON") } diff --git a/cmd/numbers_test.go b/cmd/numbers_test.go index 5e7fd5b..d91fdff 100644 --- a/cmd/numbers_test.go +++ b/cmd/numbers_test.go @@ -11,7 +11,7 @@ import ( func TestNumbersCmd(t *testing.T) { numbersBase = 10 - numbersJSONOutput = false + outputJSON = false cmd := GetRootCmd() buf := new(bytes.Buffer) @@ -33,7 +33,7 @@ func TestNumbersCmd(t *testing.T) { func TestNumbersCmdJSON(t *testing.T) { numbersBase = 10 - numbersJSONOutput = false + outputJSON = false cmd := GetRootCmd() buf := new(bytes.Buffer) diff --git a/cmd/root.go b/cmd/root.go index 775c7ae..6018cc8 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -95,4 +95,5 @@ func GetRootCmd() *cobra.Command { func init() { // fang.Execute automatically adds --version flag // We configure it via fang.WithVersion() in Execute() + rootCmd.PersistentFlags().BoolVar(&outputJSON, "json", false, "output as JSON") } diff --git a/cmd/toml2yaml.go b/cmd/toml2yaml.go index a67f0ad..8bd0a31 100644 --- a/cmd/toml2yaml.go +++ b/cmd/toml2yaml.go @@ -44,6 +44,9 @@ Input can be a string argument or piped from stdin.`, if err != nil { return cmderror.FormatParseError("toml2yaml", inputStr, err) } + if outputJSON { + return writeJSONValue(cmd.OutOrStdout(), result) + } _, err = fmt.Fprintln(cmd.OutOrStdout(), result) if err != nil { diff --git a/cmd/tomlfmt.go b/cmd/tomlfmt.go index 7c72261..620ef33 100644 --- a/cmd/tomlfmt.go +++ b/cmd/tomlfmt.go @@ -66,6 +66,9 @@ in an interactive terminal interface.`, if err != nil { return cmderror.FormatParseError("tomlfmt", inputStr, err) } + if outputJSON { + return writeJSONValue(cmd.OutOrStdout(), result) + } _, err = fmt.Fprintln(cmd.OutOrStdout(), result) if err != nil { diff --git a/cmd/tsv2md.go b/cmd/tsv2md.go index 8c8a344..c822932 100644 --- a/cmd/tsv2md.go +++ b/cmd/tsv2md.go @@ -2,6 +2,7 @@ package cmd import ( "encoding/csv" + "fmt" "strings" "github.com/skatkov/devtui/internal/cmderror" @@ -56,7 +57,15 @@ and --header to add a main heading (h1) to the output.`, return cmderror.FormatParseError("tsv2md", inputStr, err) } - csv2md.Print(csv2md.Convert(tsv2mdHeader, records, tsv2mdAlignColumns)) + rows := csv2md.Convert(tsv2mdHeader, records, tsv2mdAlignColumns) + if outputJSON { + return writeJSONValue(cmd.OutOrStdout(), strings.Join(rows, "\n")) + } + for _, row := range rows { + if _, err := fmt.Fprintln(cmd.OutOrStdout(), row); err != nil { + return err + } + } return nil }, diff --git a/cmd/urls.go b/cmd/urls.go index e8c76f8..903a377 100644 --- a/cmd/urls.go +++ b/cmd/urls.go @@ -55,10 +55,18 @@ Input can be a string argument or piped from stdin.`, } } + if outputJSON { + return writeJSONValue(cmd.OutOrStdout(), uniqueURLs) + } + // Output results if len(uniqueURLs) > 0 { - fmt.Print(strings.Join(uniqueURLs, "\n")) - fmt.Print("\n") + _, err = fmt.Fprint(cmd.OutOrStdout(), strings.Join(uniqueURLs, "\n")) + if err != nil { + return err + } + _, err = fmt.Fprint(cmd.OutOrStdout(), "\n") + return err } return nil diff --git a/cmd/uuiddecode.go b/cmd/uuiddecode.go index 9034168..5ae85d1 100644 --- a/cmd/uuiddecode.go +++ b/cmd/uuiddecode.go @@ -50,7 +50,7 @@ Input can be provided as an argument or piped from stdin.`, } fields := uuidutil.Decode(parsed) - if uuiddecodeJSONOutput { + if outputJSON { payload := uuidDecodeJSON{ UUID: parsed.String(), Fields: fields, @@ -73,9 +73,6 @@ Input can be provided as an argument or piped from stdin.`, }, } -var uuiddecodeJSONOutput bool - func init() { rootCmd.AddCommand(uuiddecodeCmd) - uuiddecodeCmd.Flags().BoolVar(&uuiddecodeJSONOutput, "json", false, "output decoded fields as JSON") } diff --git a/cmd/uuiddecode_test.go b/cmd/uuiddecode_test.go index f426fd9..ded7701 100644 --- a/cmd/uuiddecode_test.go +++ b/cmd/uuiddecode_test.go @@ -8,7 +8,7 @@ import ( ) func TestUUIDDecodeCmd(t *testing.T) { - uuiddecodeJSONOutput = false + outputJSON = false input := "4326ff5f-774d-4506-a18c-4bc50c761863" cmd := GetRootCmd() @@ -33,7 +33,7 @@ func TestUUIDDecodeCmd(t *testing.T) { } func TestUUIDDecodeCmdJSON(t *testing.T) { - uuiddecodeJSONOutput = false + outputJSON = false input := "4326ff5f-774d-4506-a18c-4bc50c761863" cmd := GetRootCmd() @@ -61,7 +61,7 @@ func TestUUIDDecodeCmdJSON(t *testing.T) { } func TestUUIDDecodeCmdNoInput(t *testing.T) { - uuiddecodeJSONOutput = false + outputJSON = false cmd := GetRootCmd() buf := new(bytes.Buffer) cmd.SetOut(buf) diff --git a/cmd/version.go b/cmd/version.go index d79f4e5..9053623 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -22,9 +22,13 @@ var versionCmd = &cobra.Command{ Use: "version", Short: "Print version information", Long: "Print version, commit and date of release for this software", - RunE: func(_ *cobra.Command, _ []string) error { - fmt.Print("devtui version " + GetVersionShort()) - return nil + RunE: func(cmd *cobra.Command, _ []string) error { + output := "devtui version " + GetVersionShort() + if outputJSON { + return writeJSONValue(cmd.OutOrStdout(), output) + } + _, err := fmt.Fprint(cmd.OutOrStdout(), output) + return err }, } diff --git a/cmd/xmlfmt.go b/cmd/xmlfmt.go index cae9c96..6b375d0 100644 --- a/cmd/xmlfmt.go +++ b/cmd/xmlfmt.go @@ -74,6 +74,9 @@ Input can be a string argument or piped from stdin.`, result := xmlfmt.FormatXML(string(data), xmlPrefix, xmlIndent, xmlNested) + if outputJSON { + return writeJSONValue(cmd.OutOrStdout(), result) + } _, err = fmt.Fprintln(cmd.OutOrStdout(), result) if err != nil { diff --git a/cmd/yaml2toml.go b/cmd/yaml2toml.go index f143200..0d46731 100644 --- a/cmd/yaml2toml.go +++ b/cmd/yaml2toml.go @@ -44,6 +44,9 @@ Input can be a string argument or piped from stdin.`, if err != nil { return cmderror.FormatParseError("yaml2toml", inputStr, err) } + if outputJSON { + return writeJSONValue(cmd.OutOrStdout(), result) + } _, err = fmt.Fprintln(cmd.OutOrStdout(), result) if err != nil {