Repository navigation
feat(tuple): add --output-file to tuple read #784
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -18,6 +18,7 @@ package tuple | |||||||||
|
|
||||||||||
| import ( | ||||||||||
| "bufio" | ||||||||||
| "bytes" | ||||||||||
| "context" | ||||||||||
| "encoding/json" | ||||||||||
| "errors" | ||||||||||
|
|
@@ -163,6 +164,81 @@ func read( | |||||||||
| return &res, nil | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // displayReadResponse prints the response to stdout, or writes it to outputFile when that is set. | ||||||||||
| func displayReadResponse(response *readResponse, outputFormat string, simpleOutput bool, outputFile string) error { | ||||||||||
| if outputFile != "" { | ||||||||||
| return writeReadResponseToFile(response, outputFormat, simpleOutput, outputFile) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if outputFormat == "csv" { | ||||||||||
| records, err := response.toCsvDTO() | ||||||||||
| if err != nil { | ||||||||||
| return fmt.Errorf("failed to convert response to csv: %w", err) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| writer := bufio.NewWriter(os.Stdout) | ||||||||||
| if err := output.MarshalCSV(records, writer, readResponseCSVHeaders...); err != nil { | ||||||||||
| return fmt.Errorf("failed to marshal csv: %w", err) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if err := writer.Flush(); err != nil { | ||||||||||
| return fmt.Errorf("failed to display csv: %w", err) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| return nil | ||||||||||
| } | ||||||||||
|
|
||||||||||
| dataPrinter := output.NewUniPrinter(outputFormat) | ||||||||||
|
|
||||||||||
| var data any | ||||||||||
|
|
||||||||||
| data = *response.complete | ||||||||||
|
|
||||||||||
| if simpleOutput || outputFormat == "simple-json" { | ||||||||||
| data = response.simple | ||||||||||
| } | ||||||||||
|
|
||||||||||
| return dataPrinter.Display(data) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // writeReadResponseToFile writes the response, in the selected output format, only to the given file. | ||||||||||
| // The file is created with 0600 permissions and is only touched once the output has been produced. | ||||||||||
| func writeReadResponseToFile(response *readResponse, outputFormat string, simpleOutput bool, outputFile string) error { | ||||||||||
| var content bytes.Buffer | ||||||||||
|
|
||||||||||
| if outputFormat == "csv" { | ||||||||||
| records, err := response.toCsvDTO() | ||||||||||
| if err != nil { | ||||||||||
| return fmt.Errorf("failed to convert response to csv: %w", err) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if err := output.MarshalCSV(records, &content, readResponseCSVHeaders...); err != nil { | ||||||||||
| return fmt.Errorf("failed to marshal csv: %w", err) | ||||||||||
| } | ||||||||||
| } else { | ||||||||||
| var data any | ||||||||||
|
|
||||||||||
| data = *response.complete | ||||||||||
|
|
||||||||||
| if simpleOutput || outputFormat == "simple-json" { | ||||||||||
| data = response.simple | ||||||||||
| } | ||||||||||
|
|
||||||||||
| rendered, err := output.Render(outputFormat, data) | ||||||||||
| if err != nil { | ||||||||||
| return fmt.Errorf("failed to render output: %w", err) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| content.Write(rendered) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if err := os.WriteFile(outputFile, content.Bytes(), 0o600); err != nil { | ||||||||||
| return fmt.Errorf("failed to write output file %q: %w", outputFile, err) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| return nil | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // readCmd represents the read command. | ||||||||||
| var readCmd = &cobra.Command{ | ||||||||||
| Use: "read", | ||||||||||
|
|
@@ -222,36 +298,9 @@ var readCmd = &cobra.Command{ | |||||||||
|
|
||||||||||
| simpleOutput, _ := cmd.Flags().GetBool("simple-output") | ||||||||||
| outputFormat, _ := cmd.Flags().GetString("output-format") | ||||||||||
| outputFile, _ := cmd.Flags().GetString("output-file") | ||||||||||
|
|
||||||||||
| if outputFormat == "csv" { | ||||||||||
| records, err := response.toCsvDTO() | ||||||||||
| if err != nil { | ||||||||||
| return fmt.Errorf("failed to convert response to csv: %w", err) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| writer := bufio.NewWriter(os.Stdout) | ||||||||||
| if err := output.MarshalCSV(records, writer, readResponseCSVHeaders...); err != nil { | ||||||||||
| return fmt.Errorf("failed to marshal csv: %w", err) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| if err := writer.Flush(); err != nil { | ||||||||||
| return fmt.Errorf("failed to display csv: %w", err) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| return nil | ||||||||||
| } | ||||||||||
|
|
||||||||||
| dataPrinter := output.NewUniPrinter(outputFormat) | ||||||||||
|
|
||||||||||
| var data any | ||||||||||
|
|
||||||||||
| data = *response.complete | ||||||||||
|
|
||||||||||
| if simpleOutput || outputFormat == "simple-json" { | ||||||||||
| data = response.simple | ||||||||||
| } | ||||||||||
|
|
||||||||||
| return dataPrinter.Display(data) | ||||||||||
| return displayReadResponse(response, outputFormat, simpleOutput, outputFile) | ||||||||||
| }, | ||||||||||
| } | ||||||||||
|
|
||||||||||
|
|
@@ -264,6 +313,8 @@ func init() { | |||||||||
| "Defaults to 100 when max-pages=0, or 50 otherwise. Max is 100.") | ||||||||||
| readCmd.Flags().String("output-format", "json", "Specifies the format for data presentation. Valid options: "+ | ||||||||||
| "json, simple-json, csv, and yaml.") | ||||||||||
| readCmd.Flags().String("output-file", "", "Write the output to this file instead of stdout. "+ | ||||||||||
| "The file is created with 0600 permissions. The selected output format is used.") | ||||||||||
|
Comment on lines
+316
to
+317
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 0600 only applies to new files. An existing file is truncated and keeps its mode (a 0644 file stays 0644), with no overwrite prompt unlike
Suggested change
|
||||||||||
| readCmd.Flags().Bool("simple-output", false, "Output data in simpler version. (It can be used by write and delete commands)") //nolint:lll | ||||||||||
| readCmd.Flags().String( | ||||||||||
| "consistency", | ||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -159,6 +159,32 @@ func (prt UniPrinter) Display(data any) error { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Render encodes data in the given output format ("yaml", anything else is JSON) without colors, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // and returns the bytes. The result always ends with a newline. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func Render(outputFormat string, data any) ([]byte, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result []byte | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| err error | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if outputFormat == "yaml" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result, err = yaml.Marshal(data) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil, fmt.Errorf("unable to marshal yaml with error %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // yaml.Marshal already ends with a newline. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return result, nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result, err = json.MarshalIndent(data, "", " ") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil, fmt.Errorf("unable to marshal json with error %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return append(result, '\n'), nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+165
to
+185
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Drop the
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // EmptyStruct is used when we wish to return an empty object. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type EmptyStruct struct{} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
writeReadResponseToFilerepeats the CSV conversion and the simple/complete selection. Only colored JSON/YAML to stdout needsUniPrinter; everything else can go through one buffer. This also gives #791 a single place to hook in. Drop thebufioimport with it. Verified: stdout and file bytes match this PR for all formats, tests and lint pass.