2023-04-19 08:28:09 +03:00
|
|
|
package cli
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io"
|
|
|
|
"os"
|
2023-05-07 05:36:34 +03:00
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/neilotoole/sq/libsq/core/stringz"
|
|
|
|
|
|
|
|
"github.com/mitchellh/go-wordwrap"
|
|
|
|
|
|
|
|
"github.com/neilotoole/sq/libsq/core/timez"
|
2023-04-19 08:28:09 +03:00
|
|
|
|
2023-05-03 15:36:10 +03:00
|
|
|
"github.com/neilotoole/sq/libsq/core/lg/lga"
|
|
|
|
|
2023-04-26 18:16:42 +03:00
|
|
|
"github.com/neilotoole/sq/libsq/core/errz"
|
|
|
|
|
|
|
|
"github.com/neilotoole/sq/libsq/core/options"
|
|
|
|
|
|
|
|
"github.com/neilotoole/sq/cli/output/format"
|
|
|
|
|
2023-04-19 08:28:09 +03:00
|
|
|
"github.com/fatih/color"
|
|
|
|
"github.com/mattn/go-colorable"
|
|
|
|
"github.com/neilotoole/sq/cli/flag"
|
|
|
|
"github.com/neilotoole/sq/cli/output"
|
|
|
|
"github.com/neilotoole/sq/cli/output/csvw"
|
|
|
|
"github.com/neilotoole/sq/cli/output/htmlw"
|
|
|
|
"github.com/neilotoole/sq/cli/output/jsonw"
|
|
|
|
"github.com/neilotoole/sq/cli/output/markdownw"
|
|
|
|
"github.com/neilotoole/sq/cli/output/raww"
|
|
|
|
"github.com/neilotoole/sq/cli/output/tablew"
|
|
|
|
"github.com/neilotoole/sq/cli/output/xlsxw"
|
|
|
|
"github.com/neilotoole/sq/cli/output/xmlw"
|
|
|
|
"github.com/neilotoole/sq/cli/output/yamlw"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
)
|
|
|
|
|
2023-04-26 18:16:42 +03:00
|
|
|
var (
|
|
|
|
OptPrintHeader = options.NewBool(
|
2023-05-03 15:36:10 +03:00
|
|
|
"header",
|
2023-05-07 05:36:34 +03:00
|
|
|
0,
|
2023-04-26 18:16:42 +03:00
|
|
|
true,
|
2023-05-07 05:36:34 +03:00
|
|
|
"Print header row",
|
2023-05-03 15:36:10 +03:00
|
|
|
`Controls whether a header row is printed. This applies only
|
|
|
|
to certain formats, such as "text" or "csv".`,
|
2023-04-26 18:16:42 +03:00
|
|
|
"format",
|
|
|
|
)
|
2023-05-07 05:36:34 +03:00
|
|
|
|
|
|
|
OptFormat = NewFormatOpt(
|
2023-04-26 18:16:42 +03:00
|
|
|
"format",
|
2023-05-03 15:36:10 +03:00
|
|
|
format.Text,
|
2023-05-07 05:36:34 +03:00
|
|
|
"Output format",
|
|
|
|
|
2023-05-07 09:01:29 +03:00
|
|
|
`Specify the output format. Some formats are only implemented for a subset of
|
2023-05-07 05:36:34 +03:00
|
|
|
sq's commands. If the specified format is not available for a particular
|
2023-05-07 11:15:02 +03:00
|
|
|
command, sq falls back to "text". Available formats:
|
2023-05-07 05:36:34 +03:00
|
|
|
|
2023-05-03 15:36:10 +03:00
|
|
|
text, csv, tsv, xlsx,
|
|
|
|
json, jsona, jsonl,
|
|
|
|
markdown, html, xml, yaml, raw`,
|
|
|
|
)
|
|
|
|
|
|
|
|
OptVerbose = options.NewBool(
|
|
|
|
"verbose",
|
2023-05-07 05:36:34 +03:00
|
|
|
'v',
|
2023-05-03 15:36:10 +03:00
|
|
|
false,
|
2023-05-07 05:36:34 +03:00
|
|
|
"Print verbose output",
|
2023-05-03 15:36:10 +03:00
|
|
|
`Print verbose output.`,
|
|
|
|
"format",
|
|
|
|
)
|
|
|
|
|
|
|
|
OptMonochrome = options.NewBool(
|
|
|
|
"monochrome",
|
2023-05-07 05:36:34 +03:00
|
|
|
'M',
|
2023-05-03 15:36:10 +03:00
|
|
|
false,
|
2023-05-07 05:36:34 +03:00
|
|
|
"Don't print color output",
|
2023-05-03 15:36:10 +03:00
|
|
|
`Don't print color output.`,
|
|
|
|
"format",
|
|
|
|
)
|
|
|
|
|
2023-05-05 20:41:22 +03:00
|
|
|
OptCompact = options.NewBool(
|
|
|
|
"compact",
|
2023-05-07 05:36:34 +03:00
|
|
|
'c',
|
2023-05-05 20:41:22 +03:00
|
|
|
false,
|
2023-05-07 05:36:34 +03:00
|
|
|
"Compact instead of pretty-printed output",
|
|
|
|
`Compact instead of pretty-printed output.`,
|
2023-04-26 18:16:42 +03:00
|
|
|
"format",
|
|
|
|
)
|
2023-05-03 15:36:10 +03:00
|
|
|
|
|
|
|
OptTuningFlushThreshold = options.NewInt(
|
|
|
|
"tuning.flush-threshold",
|
2023-05-07 05:36:34 +03:00
|
|
|
0,
|
2023-05-03 15:36:10 +03:00
|
|
|
1000,
|
2023-05-07 05:36:34 +03:00
|
|
|
"Output writer buffer flush threshold in bytes",
|
2023-05-03 15:36:10 +03:00
|
|
|
`Size in bytes after which output writers should flush any internal buffer.
|
|
|
|
Generally, it is not necessary to fiddle this knob.`,
|
|
|
|
)
|
2023-05-07 05:36:34 +03:00
|
|
|
|
|
|
|
timeLayoutsList = "Predefined values:\n" + stringz.IndentLines(
|
|
|
|
wordwrap.WrapString(strings.Join(timez.NamedLayouts(), ", "), 64),
|
|
|
|
" ")
|
|
|
|
|
|
|
|
OptDatetimeFormat = options.NewString(
|
|
|
|
"format.datetime",
|
|
|
|
0,
|
|
|
|
"RFC3339",
|
|
|
|
"Timestamp format: constant such as RFC3339 or a strftime format",
|
|
|
|
`Timestamp format. This can be one of several predefined constants such
|
|
|
|
as "RFC3339" or "Unix", or a strftime format such as "%Y-%m-%d %H:%M:%S".
|
|
|
|
|
|
|
|
`+timeLayoutsList,
|
|
|
|
)
|
|
|
|
|
|
|
|
OptDatetimeFormatAsNumber = options.NewBool(
|
|
|
|
"format.datetime.number",
|
|
|
|
0,
|
|
|
|
true,
|
|
|
|
"Render numeric datetime value as number instead of string",
|
|
|
|
`Render numeric datetime value as number instead of string, if possible.
|
|
|
|
If format.datetime renders a numeric value (e.g. a Unix timestamp such
|
|
|
|
as "1591843854"), that value is typically rendered as a string. For some output
|
|
|
|
formats, such as JSON, it can be useful to instead render the value as a naked
|
|
|
|
number instead of a string. Note that this option is no-op if the rendered value
|
|
|
|
is not an integer.
|
|
|
|
|
|
|
|
format.datetime.number=false
|
|
|
|
[{"first_name":"PENELOPE","last_update":"1591843854"}]
|
|
|
|
format.datetime.number=true
|
|
|
|
[{"first_name":"PENELOPE","last_update":1591843854}]
|
|
|
|
`,
|
|
|
|
)
|
|
|
|
|
|
|
|
// FIXME: add opts OptDateFormatNumber and OptTimeFormatNumber.
|
|
|
|
// The doc should note that OptDatetimeFormatNumber has primacy, and
|
|
|
|
// to setting that flag instead.
|
|
|
|
|
|
|
|
OptDateFormat = options.NewString(
|
|
|
|
"format.date",
|
|
|
|
0,
|
|
|
|
"DateOnly",
|
|
|
|
"Date format: constant such as DateOnly or a strftime format",
|
|
|
|
`Date format. This can be one of several predefined constants such
|
|
|
|
as "DateOnly" or "Unix", or a strftime format such as "%Y-%m-%d".
|
|
|
|
Note that date values are sometimes programmatically indistinguishable
|
|
|
|
from datetime values. In that situation, use format.datetime instead.
|
|
|
|
|
|
|
|
`+timeLayoutsList,
|
|
|
|
)
|
|
|
|
|
|
|
|
OptDateFormatAsNumber = options.NewBool(
|
|
|
|
"format.date.number",
|
|
|
|
0,
|
|
|
|
true,
|
|
|
|
"Render numeric date value as number instead of string",
|
|
|
|
`Render numeric date value as number instead of string, if possible.
|
|
|
|
If format.date renders a numeric value (e.g. a year such as "1979"), that value
|
|
|
|
is typically rendered as a string. For some output formats, such as JSON, it can
|
|
|
|
be useful to instead render the value as a naked number instead of a string.
|
|
|
|
Note that this option is no-op if the rendered value is not an integer.
|
|
|
|
|
|
|
|
format.date.number=false
|
|
|
|
[{"first_name":"PENELOPE","birth_year":"1979"}]
|
|
|
|
format.date.number=true
|
|
|
|
[{"first_name":"PENELOPE","birth_year":1979}]
|
|
|
|
`,
|
|
|
|
)
|
|
|
|
|
|
|
|
OptTimeFormat = options.NewString(
|
|
|
|
"format.time",
|
|
|
|
0,
|
|
|
|
"TimeOnly",
|
|
|
|
"Time format: constant such as TimeOnly or a strftime format",
|
|
|
|
`Time format. This can be one of several predefined constants such
|
|
|
|
as "TimeOnly" or "Unix", or a strftime format such as "%Y-%m-%d".
|
|
|
|
Note that time values are sometimes programmatically indistinguishable
|
|
|
|
from datetime values. In that situation, use format.datetime instead.
|
|
|
|
|
|
|
|
`+timeLayoutsList,
|
|
|
|
)
|
|
|
|
|
|
|
|
OptTimeFormatAsNumber = options.NewBool(
|
|
|
|
"format.time.number",
|
|
|
|
0,
|
|
|
|
true,
|
|
|
|
"Render numeric time value as number instead of string",
|
|
|
|
`Render numeric time value as number instead of string, if possible.
|
|
|
|
If format.time renders a numeric value (e.g. "59"), that value
|
|
|
|
is typically rendered as a string. For some output formats, such as JSON, it can
|
|
|
|
be useful to instead render the value as a naked number instead of a string.
|
|
|
|
Note that this option is no-op if the rendered value is not an integer.
|
|
|
|
|
|
|
|
format.time.number=false
|
|
|
|
[{"first_name":"PENELOPE","favorite_minute":"59"}]
|
|
|
|
format.time.number=true
|
|
|
|
[{"first_name":"PENELOPE","favorite_minute":59}]
|
|
|
|
`,
|
|
|
|
)
|
2023-04-26 18:16:42 +03:00
|
|
|
)
|
|
|
|
|
2023-04-19 08:28:09 +03:00
|
|
|
// writers is a container for the various output writers.
|
|
|
|
type writers struct {
|
2023-04-22 06:36:32 +03:00
|
|
|
pr *output.Printing
|
2023-04-19 08:28:09 +03:00
|
|
|
|
|
|
|
recordw output.RecordWriter
|
|
|
|
metaw output.MetadataWriter
|
|
|
|
srcw output.SourceWriter
|
|
|
|
errw output.ErrorWriter
|
|
|
|
pingw output.PingWriter
|
|
|
|
versionw output.VersionWriter
|
|
|
|
configw output.ConfigWriter
|
|
|
|
}
|
|
|
|
|
|
|
|
// newWriters returns a writers instance configured per defaults and/or
|
|
|
|
// flags from cmd. The returned out2/errOut2 values may differ
|
|
|
|
// from the out/errOut args (e.g. decorated to support colorization).
|
2023-04-26 18:16:42 +03:00
|
|
|
func newWriters(cmd *cobra.Command, opts options.Options, out, errOut io.Writer,
|
2023-04-19 08:28:09 +03:00
|
|
|
) (w *writers, out2, errOut2 io.Writer) {
|
2023-04-22 06:36:32 +03:00
|
|
|
var pr *output.Printing
|
2023-04-26 18:16:42 +03:00
|
|
|
pr, out2, errOut2 = getPrinting(cmd, opts, out, errOut)
|
2023-04-30 17:18:56 +03:00
|
|
|
log := logFrom(cmd)
|
2023-04-19 08:28:09 +03:00
|
|
|
|
|
|
|
// Package tablew has writer impls for each of the writer interfaces,
|
|
|
|
// so we use its writers as the baseline. Later we check the format
|
|
|
|
// flags and set the various writer fields depending upon which
|
|
|
|
// writers the format implements.
|
|
|
|
w = &writers{
|
2023-04-22 06:36:32 +03:00
|
|
|
pr: pr,
|
|
|
|
recordw: tablew.NewRecordWriter(out2, pr),
|
|
|
|
metaw: tablew.NewMetadataWriter(out2, pr),
|
|
|
|
srcw: tablew.NewSourceWriter(out2, pr),
|
|
|
|
pingw: tablew.NewPingWriter(out2, pr),
|
|
|
|
errw: tablew.NewErrorWriter(errOut2, pr),
|
|
|
|
versionw: tablew.NewVersionWriter(out2, pr),
|
|
|
|
configw: tablew.NewConfigWriter(out2, pr),
|
2023-04-19 08:28:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Invoke getFormat to see if the format was specified
|
|
|
|
// via config or flag.
|
2023-05-03 15:36:10 +03:00
|
|
|
fm := getFormat(cmd, opts) // FIXME: is this still needed, or use standard opts mechanism?
|
2023-04-19 08:28:09 +03:00
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
//nolint:exhaustive
|
2023-04-26 18:16:42 +03:00
|
|
|
switch fm {
|
2023-04-19 08:28:09 +03:00
|
|
|
default:
|
|
|
|
// No format specified, use JSON
|
2023-04-22 06:36:32 +03:00
|
|
|
w.recordw = jsonw.NewStdRecordWriter(out2, pr)
|
|
|
|
w.metaw = jsonw.NewMetadataWriter(out2, pr)
|
|
|
|
w.srcw = jsonw.NewSourceWriter(out2, pr)
|
|
|
|
w.errw = jsonw.NewErrorWriter(log, errOut2, pr)
|
|
|
|
w.versionw = jsonw.NewVersionWriter(out2, pr)
|
|
|
|
w.pingw = jsonw.NewPingWriter(out2, pr)
|
|
|
|
w.configw = jsonw.NewConfigWriter(out2, pr)
|
2023-04-19 08:28:09 +03:00
|
|
|
|
2023-05-03 15:36:10 +03:00
|
|
|
case format.Text:
|
2023-04-19 08:28:09 +03:00
|
|
|
// Table is the base format, already set above, no need to do anything.
|
|
|
|
|
2023-04-26 18:16:42 +03:00
|
|
|
case format.TSV:
|
2023-05-07 05:36:34 +03:00
|
|
|
w.recordw = csvw.NewRecordWriter(out2, pr, csvw.Tab)
|
2023-04-19 08:28:09 +03:00
|
|
|
w.pingw = csvw.NewPingWriter(out2, csvw.Tab)
|
|
|
|
|
2023-04-26 18:16:42 +03:00
|
|
|
case format.CSV:
|
2023-05-07 05:36:34 +03:00
|
|
|
w.recordw = csvw.NewRecordWriter(out2, pr, csvw.Comma)
|
2023-04-19 08:28:09 +03:00
|
|
|
w.pingw = csvw.NewPingWriter(out2, csvw.Comma)
|
|
|
|
|
2023-04-26 18:16:42 +03:00
|
|
|
case format.XML:
|
2023-04-22 06:36:32 +03:00
|
|
|
w.recordw = xmlw.NewRecordWriter(out2, pr)
|
2023-04-19 08:28:09 +03:00
|
|
|
|
2023-04-26 18:16:42 +03:00
|
|
|
case format.XLSX:
|
2023-05-07 05:36:34 +03:00
|
|
|
w.recordw = xlsxw.NewRecordWriter(out2, pr)
|
2023-04-19 08:28:09 +03:00
|
|
|
|
2023-04-26 18:16:42 +03:00
|
|
|
case format.Raw:
|
2023-05-07 05:36:34 +03:00
|
|
|
w.recordw = raww.NewRecordWriter(out2, pr)
|
2023-04-19 08:28:09 +03:00
|
|
|
|
2023-04-26 18:16:42 +03:00
|
|
|
case format.HTML:
|
2023-05-07 05:36:34 +03:00
|
|
|
w.recordw = htmlw.NewRecordWriter(out2, pr)
|
2023-04-19 08:28:09 +03:00
|
|
|
|
2023-04-26 18:16:42 +03:00
|
|
|
case format.Markdown:
|
2023-05-07 05:36:34 +03:00
|
|
|
w.recordw = markdownw.NewRecordWriter(out2, pr)
|
2023-04-19 08:28:09 +03:00
|
|
|
|
2023-04-26 18:16:42 +03:00
|
|
|
case format.JSONA:
|
2023-04-22 06:36:32 +03:00
|
|
|
w.recordw = jsonw.NewArrayRecordWriter(out2, pr)
|
2023-04-19 08:28:09 +03:00
|
|
|
|
2023-04-26 18:16:42 +03:00
|
|
|
case format.JSONL:
|
2023-04-22 06:36:32 +03:00
|
|
|
w.recordw = jsonw.NewObjectRecordWriter(out2, pr)
|
2023-04-19 08:28:09 +03:00
|
|
|
|
2023-04-26 18:16:42 +03:00
|
|
|
case format.YAML:
|
2023-04-22 06:36:32 +03:00
|
|
|
w.configw = yamlw.NewConfigWriter(out2, pr)
|
|
|
|
w.metaw = yamlw.NewMetadataWriter(out2, pr)
|
2023-05-05 17:32:50 +03:00
|
|
|
w.srcw = yamlw.NewSourceWriter(out2, pr)
|
2023-04-19 08:28:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return w, out2, errOut2
|
|
|
|
}
|
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
// getPrinting returns a Printing instance and
|
2023-04-19 08:28:09 +03:00
|
|
|
// colorable or non-colorable writers. It is permissible
|
2023-04-26 18:16:42 +03:00
|
|
|
// for the cmd arg to be nil. The caller should use the returned
|
|
|
|
// io.Writer instances instead of the supplied writers, as they
|
|
|
|
// may be decorated for dealing with color, etc.
|
2023-05-03 15:36:10 +03:00
|
|
|
// The supplied opts must already have flags merged into it
|
|
|
|
// via getOptionsFromCmd.
|
|
|
|
func getPrinting(cmd *cobra.Command, opts options.Options, out, errOut io.Writer,
|
2023-04-22 06:36:32 +03:00
|
|
|
) (pr *output.Printing, out2, errOut2 io.Writer) {
|
|
|
|
pr = output.NewPrinting()
|
2023-04-19 08:28:09 +03:00
|
|
|
|
2023-05-07 05:36:34 +03:00
|
|
|
pr.FormatDatetime = timez.FormatFunc(OptDatetimeFormat.Get(opts))
|
|
|
|
pr.FormatDatetimeAsNumber = OptDatetimeFormatAsNumber.Get(opts)
|
|
|
|
pr.FormatTime = timez.FormatFunc(OptTimeFormat.Get(opts))
|
|
|
|
pr.FormatTimeAsNumber = OptTimeFormatAsNumber.Get(opts)
|
|
|
|
pr.FormatDate = timez.FormatFunc(OptDateFormat.Get(opts))
|
|
|
|
pr.FormatDateAsNumber = OptDateFormatAsNumber.Get(opts)
|
|
|
|
|
2023-05-03 15:36:10 +03:00
|
|
|
pr.Verbose = OptVerbose.Get(opts)
|
|
|
|
pr.FlushThreshold = OptTuningFlushThreshold.Get(opts)
|
2023-05-05 20:41:22 +03:00
|
|
|
pr.Compact = OptCompact.Get(opts)
|
2023-04-19 08:28:09 +03:00
|
|
|
|
2023-04-26 18:16:42 +03:00
|
|
|
switch {
|
|
|
|
case cmdFlagChanged(cmd, flag.Header):
|
2023-04-22 06:36:32 +03:00
|
|
|
pr.ShowHeader, _ = cmd.Flags().GetBool(flag.Header)
|
2023-04-26 18:16:42 +03:00
|
|
|
case cmdFlagChanged(cmd, flag.NoHeader):
|
|
|
|
b, _ := cmd.Flags().GetBool(flag.NoHeader)
|
|
|
|
pr.ShowHeader = !b
|
|
|
|
case opts != nil:
|
|
|
|
pr.ShowHeader = OptPrintHeader.Get(opts)
|
2023-04-19 08:28:09 +03:00
|
|
|
}
|
|
|
|
|
2023-05-03 15:36:10 +03:00
|
|
|
colorize := !OptMonochrome.Get(opts)
|
2023-04-19 08:28:09 +03:00
|
|
|
|
|
|
|
if cmdFlagChanged(cmd, flag.Output) {
|
|
|
|
// We're outputting to a file, thus no color.
|
|
|
|
colorize = false
|
|
|
|
}
|
|
|
|
|
|
|
|
if !colorize {
|
2023-05-03 15:36:10 +03:00
|
|
|
color.NoColor = true
|
2023-04-22 06:36:32 +03:00
|
|
|
pr.EnableColor(false)
|
2023-04-19 08:28:09 +03:00
|
|
|
out2 = out
|
|
|
|
errOut2 = errOut
|
2023-04-22 06:36:32 +03:00
|
|
|
return pr, out2, errOut2
|
2023-04-19 08:28:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// We do want to colorize
|
|
|
|
if !isColorTerminal(out) {
|
|
|
|
// But out can't be colorized.
|
|
|
|
color.NoColor = true
|
2023-04-22 06:36:32 +03:00
|
|
|
pr.EnableColor(false)
|
2023-04-19 08:28:09 +03:00
|
|
|
out2, errOut2 = out, errOut
|
2023-04-22 06:36:32 +03:00
|
|
|
return pr, out2, errOut2
|
2023-04-19 08:28:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// out can be colorized.
|
|
|
|
color.NoColor = false
|
2023-04-22 06:36:32 +03:00
|
|
|
pr.EnableColor(true)
|
2023-04-19 08:28:09 +03:00
|
|
|
out2 = colorable.NewColorable(out.(*os.File))
|
|
|
|
|
|
|
|
// Check if we can colorize errOut
|
|
|
|
if isColorTerminal(errOut) {
|
|
|
|
errOut2 = colorable.NewColorable(errOut.(*os.File))
|
|
|
|
} else {
|
|
|
|
// errOut2 can't be colorized, but since we're colorizing
|
|
|
|
// out, we'll apply the non-colorable filter to errOut.
|
|
|
|
errOut2 = colorable.NewNonColorable(errOut)
|
|
|
|
}
|
|
|
|
|
2023-05-03 15:36:10 +03:00
|
|
|
logFrom(cmd).Debug("Constructed output.Printing", lga.Val, pr)
|
|
|
|
|
2023-04-22 06:36:32 +03:00
|
|
|
return pr, out2, errOut2
|
2023-04-19 08:28:09 +03:00
|
|
|
}
|
|
|
|
|
2023-05-05 17:32:50 +03:00
|
|
|
func getFormat(cmd *cobra.Command, o options.Options) format.Format {
|
2023-04-26 18:16:42 +03:00
|
|
|
var fm format.Format
|
2023-04-19 08:28:09 +03:00
|
|
|
|
|
|
|
switch {
|
|
|
|
case cmdFlagChanged(cmd, flag.TSV):
|
2023-04-26 18:16:42 +03:00
|
|
|
fm = format.TSV
|
2023-04-19 08:28:09 +03:00
|
|
|
case cmdFlagChanged(cmd, flag.CSV):
|
2023-04-26 18:16:42 +03:00
|
|
|
fm = format.CSV
|
2023-04-19 08:28:09 +03:00
|
|
|
case cmdFlagChanged(cmd, flag.XLSX):
|
2023-04-26 18:16:42 +03:00
|
|
|
fm = format.XLSX
|
2023-04-19 08:28:09 +03:00
|
|
|
case cmdFlagChanged(cmd, flag.XML):
|
2023-04-26 18:16:42 +03:00
|
|
|
fm = format.XML
|
2023-04-19 08:28:09 +03:00
|
|
|
case cmdFlagChanged(cmd, flag.Raw):
|
2023-04-26 18:16:42 +03:00
|
|
|
fm = format.Raw
|
2023-04-19 08:28:09 +03:00
|
|
|
case cmdFlagChanged(cmd, flag.HTML):
|
2023-04-26 18:16:42 +03:00
|
|
|
fm = format.HTML
|
2023-04-19 08:28:09 +03:00
|
|
|
case cmdFlagChanged(cmd, flag.Markdown):
|
2023-04-26 18:16:42 +03:00
|
|
|
fm = format.Markdown
|
2023-05-03 15:36:10 +03:00
|
|
|
case cmdFlagChanged(cmd, flag.Text):
|
|
|
|
fm = format.Text
|
2023-04-19 08:28:09 +03:00
|
|
|
case cmdFlagChanged(cmd, flag.JSONL):
|
2023-04-26 18:16:42 +03:00
|
|
|
fm = format.JSONL
|
2023-04-19 08:28:09 +03:00
|
|
|
case cmdFlagChanged(cmd, flag.JSONA):
|
2023-04-26 18:16:42 +03:00
|
|
|
fm = format.JSONA
|
2023-04-19 08:28:09 +03:00
|
|
|
case cmdFlagChanged(cmd, flag.JSON):
|
2023-04-26 18:16:42 +03:00
|
|
|
fm = format.JSON
|
2023-04-19 08:28:09 +03:00
|
|
|
case cmdFlagChanged(cmd, flag.YAML):
|
2023-04-26 18:16:42 +03:00
|
|
|
fm = format.YAML
|
2023-04-19 08:28:09 +03:00
|
|
|
default:
|
|
|
|
// no format flag, use the config value
|
2023-05-07 05:36:34 +03:00
|
|
|
fm = OptFormat.Get(o)
|
2023-04-26 18:16:42 +03:00
|
|
|
}
|
|
|
|
return fm
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ options.Opt = FormatOpt{}
|
|
|
|
|
2023-04-30 17:18:56 +03:00
|
|
|
// NewFormatOpt returns a new FormatOpt instance.
|
2023-05-07 05:36:34 +03:00
|
|
|
func NewFormatOpt(key string, defaultVal format.Format, usage, help string) FormatOpt {
|
|
|
|
opt := options.NewBaseOpt(key, 0, usage, help)
|
|
|
|
return FormatOpt{BaseOpt: opt, defaultVal: defaultVal}
|
2023-04-26 18:16:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// FormatOpt is an options.Opt for format.Format.
|
|
|
|
type FormatOpt struct {
|
2023-05-07 05:36:34 +03:00
|
|
|
options.BaseOpt
|
2023-04-26 18:16:42 +03:00
|
|
|
defaultVal format.Format
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process implements options.Processor. It converts matching
|
2023-04-30 17:18:56 +03:00
|
|
|
// string values in o into format.Format. If no match found,
|
2023-04-26 18:16:42 +03:00
|
|
|
// the input arg is returned unchanged. Otherwise, a clone is
|
|
|
|
// returned.
|
2023-04-30 17:18:56 +03:00
|
|
|
func (op FormatOpt) Process(o options.Options) (options.Options, error) {
|
|
|
|
if o == nil {
|
2023-04-26 18:16:42 +03:00
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2023-05-07 05:36:34 +03:00
|
|
|
key := op.Key()
|
|
|
|
v, ok := o[key]
|
2023-04-26 18:16:42 +03:00
|
|
|
if !ok || v == nil {
|
2023-04-30 17:18:56 +03:00
|
|
|
return o, nil
|
2023-04-26 18:16:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// v should be a string
|
2023-05-01 06:59:34 +03:00
|
|
|
switch v := v.(type) {
|
|
|
|
case string:
|
|
|
|
// continue below
|
|
|
|
case format.Format:
|
|
|
|
return o, nil
|
|
|
|
default:
|
|
|
|
return nil, errz.Errorf("option {%s} should be {%T} or {%T} but got {%T}: %v",
|
2023-05-07 05:36:34 +03:00
|
|
|
key, format.Format(""), "", v, v)
|
2023-05-01 06:59:34 +03:00
|
|
|
}
|
|
|
|
|
2023-04-26 18:16:42 +03:00
|
|
|
var s string
|
|
|
|
s, ok = v.(string)
|
|
|
|
if !ok {
|
|
|
|
return nil, errz.Errorf("option {%s} should be {%T} but got {%T}: %v",
|
2023-05-07 05:36:34 +03:00
|
|
|
key, s, v, v)
|
2023-04-26 18:16:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
var f format.Format
|
|
|
|
if err := f.UnmarshalText([]byte(s)); err != nil {
|
2023-05-07 05:36:34 +03:00
|
|
|
return nil, errz.Wrapf(err, "option {%s} is not a valid {%T}", key, f)
|
2023-04-26 18:16:42 +03:00
|
|
|
}
|
|
|
|
|
2023-04-30 17:18:56 +03:00
|
|
|
o = o.Clone()
|
2023-05-07 05:36:34 +03:00
|
|
|
o[key] = f
|
2023-04-30 17:18:56 +03:00
|
|
|
return o, nil
|
2023-04-26 18:16:42 +03:00
|
|
|
}
|
|
|
|
|
2023-04-30 17:18:56 +03:00
|
|
|
// GetAny implements options.Opt.
|
|
|
|
func (op FormatOpt) GetAny(o options.Options) any {
|
|
|
|
return op.Get(o)
|
|
|
|
}
|
|
|
|
|
2023-05-07 05:36:34 +03:00
|
|
|
// DefaultAny implements options.Opt.
|
|
|
|
func (op FormatOpt) DefaultAny() any {
|
|
|
|
return op.defaultVal
|
|
|
|
}
|
|
|
|
|
2023-04-30 17:18:56 +03:00
|
|
|
// Get returns op's value in o. If o is nil, or no value
|
|
|
|
// is set, op's default value is returned.
|
|
|
|
func (op FormatOpt) Get(o options.Options) format.Format {
|
|
|
|
if o == nil {
|
|
|
|
return op.defaultVal
|
2023-04-26 18:16:42 +03:00
|
|
|
}
|
|
|
|
|
2023-05-07 05:36:34 +03:00
|
|
|
v, ok := o[op.Key()]
|
2023-04-26 18:16:42 +03:00
|
|
|
if !ok {
|
2023-04-30 17:18:56 +03:00
|
|
|
return op.defaultVal
|
2023-04-26 18:16:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
var f format.Format
|
|
|
|
f, ok = v.(format.Format)
|
|
|
|
if !ok {
|
2023-04-30 17:18:56 +03:00
|
|
|
return op.defaultVal
|
2023-04-26 18:16:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return f
|
2023-04-19 08:28:09 +03:00
|
|
|
}
|