diff --git a/cmd/root.go b/cmd/root.go index 7babf1b5..d6fb7fae 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -97,7 +97,8 @@ yq -P -oy sample.json panic(err) } - rootCmd.PersistentFlags().StringVarP(&outputFormat, "output-format", "o", "auto", "[auto|a|yaml|y|json|j|props|p|xml|x|tsv|t|csv|c] output format type.") + rootCmd.PersistentFlags().StringVarP(&outputFormat, "output-format", "o", "auto", fmt.Sprintf("[auto|a|%v] output format type.", yqlib.GetAvailableOutputFormatString())) + if err = rootCmd.RegisterFlagCompletionFunc("output-format", cobra.FixedCompletions([]string{"auto", "yaml", "json", "props", "xml", "tsv", "csv"}, cobra.ShellCompDirectiveNoFileComp)); err != nil { panic(err) } diff --git a/cmd/utils.go b/cmd/utils.go index 0a13c31f..769e421b 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -153,7 +153,7 @@ func createDecoder(format yqlib.InputFormat, evaluateTogether bool) (yqlib.Decod return nil, fmt.Errorf("invalid decoder: %v", format) } -func configurePrinterWriter(format yqlib.PrinterOutputFormat, out io.Writer) (yqlib.PrinterWriter, error) { +func configurePrinterWriter(format *yqlib.PrinterOutputFormat, out io.Writer) (yqlib.PrinterWriter, error) { var printerWriter yqlib.PrinterWriter @@ -182,7 +182,7 @@ func configureEncoder() (yqlib.Encoder, error) { return yqlibEncoder, err } -func createEncoder(format yqlib.PrinterOutputFormat) (yqlib.Encoder, error) { +func createEncoder(format *yqlib.PrinterOutputFormat) (yqlib.Encoder, error) { switch format { case yqlib.JSONOutputFormat: return yqlib.NewJSONEncoder(indent, colorsEnabled, unwrapScalar), nil diff --git a/pkg/yqlib/json_test.go b/pkg/yqlib/json_test.go index ff81618c..49ce2330 100644 --- a/pkg/yqlib/json_test.go +++ b/pkg/yqlib/json_test.go @@ -416,7 +416,7 @@ func documentJSONDecodeScenario(t *testing.T, w *bufio.Writer, s formatScenario) writeOrPanic(w, "will output\n") var output bytes.Buffer - printer := NewSimpleYamlPrinter(bufio.NewWriter(&output), YamlOutputFormat, true, false, 2, true) + printer := NewSimpleYamlPrinter(bufio.NewWriter(&output), true, false, 2, true) node := decodeJSON(t, s.input) diff --git a/pkg/yqlib/lexer_participle.go b/pkg/yqlib/lexer_participle.go index 1b365d88..b45a60bc 100644 --- a/pkg/yqlib/lexer_participle.go +++ b/pkg/yqlib/lexer_participle.go @@ -496,7 +496,7 @@ func numberValue() yqAction { } } -func encodeParseIndent(outputFormat PrinterOutputFormat) yqAction { +func encodeParseIndent(outputFormat *PrinterOutputFormat) yqAction { return func(rawToken lexer.Token) (*token, error) { value := rawToken.Value var indent, errParsingInt = extractNumberParameter(value) @@ -510,7 +510,7 @@ func encodeParseIndent(outputFormat PrinterOutputFormat) yqAction { } } -func encodeWithIndent(outputFormat PrinterOutputFormat, indent int) yqAction { +func encodeWithIndent(outputFormat *PrinterOutputFormat, indent int) yqAction { prefs := encoderPreferences{format: outputFormat, indent: indent} return opTokenWithPrefs(encodeOpType, nil, prefs) } diff --git a/pkg/yqlib/operator_encoder_decoder.go b/pkg/yqlib/operator_encoder_decoder.go index c68a2d3d..68202c54 100644 --- a/pkg/yqlib/operator_encoder_decoder.go +++ b/pkg/yqlib/operator_encoder_decoder.go @@ -9,7 +9,7 @@ import ( "strings" ) -func configureEncoder(format PrinterOutputFormat, indent int) Encoder { +func configureEncoder(format *PrinterOutputFormat, indent int) Encoder { switch format { case JSONOutputFormat: return NewJSONEncoder(indent, false, false) @@ -48,7 +48,7 @@ func encodeToString(candidate *CandidateNode, prefs encoderPreferences) (string, } type encoderPreferences struct { - format PrinterOutputFormat + format *PrinterOutputFormat indent int } diff --git a/pkg/yqlib/operators_test.go b/pkg/yqlib/operators_test.go index b6f37aa1..24e07532 100644 --- a/pkg/yqlib/operators_test.go +++ b/pkg/yqlib/operators_test.go @@ -40,7 +40,7 @@ func TestMain(m *testing.M) { os.Exit(code) } -func NewSimpleYamlPrinter(writer io.Writer, _ PrinterOutputFormat, unwrapScalar bool, colorsEnabled bool, indent int, printDocSeparators bool) Printer { +func NewSimpleYamlPrinter(writer io.Writer, unwrapScalar bool, colorsEnabled bool, indent int, printDocSeparators bool) Printer { prefs := NewDefaultYamlPreferences() prefs.PrintDocSeparators = printDocSeparators prefs.UnwrapScalar = unwrapScalar @@ -132,7 +132,7 @@ func testScenario(t *testing.T, s *expressionScenario) { func resultToString(t *testing.T, n *CandidateNode) string { var valueBuffer bytes.Buffer log.Debugf("printing result %v", NodeToString(n)) - printer := NewSimpleYamlPrinter(bufio.NewWriter(&valueBuffer), YamlOutputFormat, true, false, 4, true) + printer := NewSimpleYamlPrinter(bufio.NewWriter(&valueBuffer), true, false, 4, true) err := printer.PrintResults(n.AsList()) if err != nil { @@ -182,7 +182,7 @@ func copySnippet(source string, out *os.File) error { func formatYaml(yaml string, filename string) string { var output bytes.Buffer - printer := NewSimpleYamlPrinter(bufio.NewWriter(&output), YamlOutputFormat, true, false, 2, true) + printer := NewSimpleYamlPrinter(bufio.NewWriter(&output), true, false, 2, true) node, err := getExpressionParser().ParseExpression(".. style= \"\"") if err != nil { @@ -331,7 +331,7 @@ func documentInput(w *bufio.Writer, s expressionScenario) (string, string) { func documentOutput(t *testing.T, w *bufio.Writer, s expressionScenario, formattedDoc string, formattedDoc2 string) { var output bytes.Buffer var err error - printer := NewSimpleYamlPrinter(bufio.NewWriter(&output), YamlOutputFormat, true, false, 2, true) + printer := NewSimpleYamlPrinter(bufio.NewWriter(&output), true, false, 2, true) node, err := getExpressionParser().ParseExpression(s.expression) if err != nil { diff --git a/pkg/yqlib/printer.go b/pkg/yqlib/printer.go index f4af16c2..86b68b11 100644 --- a/pkg/yqlib/printer.go +++ b/pkg/yqlib/printer.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "regexp" + "strings" ) type Printer interface { @@ -17,46 +18,75 @@ type Printer interface { SetNulSepOutput(nulSepOutput bool) } -type PrinterOutputFormat uint32 +type PrinterOutputFormat struct { + FormalName string + Names []string +} -const ( - YamlOutputFormat = 1 << iota - JSONOutputFormat - PropsOutputFormat - CSVOutputFormat - TSVOutputFormat - XMLOutputFormat - Base64OutputFormat - UriOutputFormat - ShOutputFormat - TomlOutputFormat - ShellVariablesOutputFormat - LuaOutputFormat -) +var YamlOutputFormat = &PrinterOutputFormat{"yaml", []string{"y", "yml"}} +var JSONOutputFormat = &PrinterOutputFormat{"json", []string{"j"}} +var PropsOutputFormat = &PrinterOutputFormat{"props", []string{"p", "properties"}} +var CSVOutputFormat = &PrinterOutputFormat{"csv", []string{"c"}} +var TSVOutputFormat = &PrinterOutputFormat{"tsv", []string{"t"}} +var XMLOutputFormat = &PrinterOutputFormat{"xml", []string{"x"}} -func OutputFormatFromString(format string) (PrinterOutputFormat, error) { - switch format { - case "yaml", "y", "yml": - return YamlOutputFormat, nil - case "json", "j": - return JSONOutputFormat, nil - case "props", "p", "properties": - return PropsOutputFormat, nil - case "csv", "c": - return CSVOutputFormat, nil - case "tsv", "t": - return TSVOutputFormat, nil - case "xml", "x": - return XMLOutputFormat, nil - case "toml": - return TomlOutputFormat, nil - case "shell", "s", "sh": - return ShellVariablesOutputFormat, nil - case "lua", "l": - return LuaOutputFormat, nil - default: - return 0, fmt.Errorf("unknown format '%v' please use [yaml|json|props|csv|tsv|xml|toml|shell|lua]", format) +var Base64OutputFormat = &PrinterOutputFormat{} +var UriOutputFormat = &PrinterOutputFormat{} +var ShOutputFormat = &PrinterOutputFormat{} + +var TomlOutputFormat = &PrinterOutputFormat{"toml", []string{}} +var ShellVariablesOutputFormat = &PrinterOutputFormat{"shell", []string{"s", "sh"}} + +var LuaOutputFormat = &PrinterOutputFormat{"lua", []string{"l"}} + +var Formats = []*PrinterOutputFormat{ + YamlOutputFormat, + JSONOutputFormat, + PropsOutputFormat, + CSVOutputFormat, + TSVOutputFormat, + XMLOutputFormat, + Base64OutputFormat, + UriOutputFormat, + ShOutputFormat, + TomlOutputFormat, + ShellVariablesOutputFormat, + LuaOutputFormat, +} + +func (f *PrinterOutputFormat) MatchesName(name string) bool { + if f.FormalName == name { + return true } + for _, n := range f.Names { + if n == name { + return true + } + } + return false +} + +func OutputFormatFromString(format string) (*PrinterOutputFormat, error) { + for _, printerFormat := range Formats { + if printerFormat.MatchesName(format) { + return printerFormat, nil + } + } + + return nil, fmt.Errorf("unknown format '%v' please use [%v]", format, GetAvailableOutputFormatString()) +} + +func GetAvailableOutputFormatString() string { + var formats = []string{} + for _, printerFormat := range Formats { + if printerFormat.FormalName != "" { + formats = append(formats, printerFormat.FormalName) + } + if len(printerFormat.Names) >= 1 { + formats = append(formats, printerFormat.Names[0]) + } + } + return strings.Join(formats, "|") } type resultsPrinter struct { diff --git a/pkg/yqlib/printer_test.go b/pkg/yqlib/printer_test.go index 371bb6f0..49e6ce13 100644 --- a/pkg/yqlib/printer_test.go +++ b/pkg/yqlib/printer_test.go @@ -36,7 +36,7 @@ func nodeToList(candidate *CandidateNode) *list.List { func TestPrinterMultipleDocsInSequenceOnly(t *testing.T) { var output bytes.Buffer var writer = bufio.NewWriter(&output) - printer := NewSimpleYamlPrinter(writer, YamlOutputFormat, true, false, 2, true) + printer := NewSimpleYamlPrinter(writer, true, false, 2, true) inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0, NewYamlDecoder(ConfiguredYamlPreferences)) if err != nil { @@ -74,7 +74,7 @@ func TestPrinterMultipleDocsInSequenceOnly(t *testing.T) { func TestPrinterMultipleDocsInSequenceWithLeadingContent(t *testing.T) { var output bytes.Buffer var writer = bufio.NewWriter(&output) - printer := NewSimpleYamlPrinter(writer, YamlOutputFormat, true, false, 2, true) + printer := NewSimpleYamlPrinter(writer, true, false, 2, true) inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0, NewYamlDecoder(ConfiguredYamlPreferences)) if err != nil { @@ -116,7 +116,7 @@ func TestPrinterMultipleDocsInSequenceWithLeadingContent(t *testing.T) { func TestPrinterMultipleFilesInSequence(t *testing.T) { var output bytes.Buffer var writer = bufio.NewWriter(&output) - printer := NewSimpleYamlPrinter(writer, YamlOutputFormat, true, false, 2, true) + printer := NewSimpleYamlPrinter(writer, true, false, 2, true) inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0, NewYamlDecoder(ConfiguredYamlPreferences)) if err != nil { @@ -163,7 +163,7 @@ func TestPrinterMultipleFilesInSequence(t *testing.T) { func TestPrinterMultipleFilesInSequenceWithLeadingContent(t *testing.T) { var output bytes.Buffer var writer = bufio.NewWriter(&output) - printer := NewSimpleYamlPrinter(writer, YamlOutputFormat, true, false, 2, true) + printer := NewSimpleYamlPrinter(writer, true, false, 2, true) inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0, NewYamlDecoder(ConfiguredYamlPreferences)) if err != nil { @@ -213,7 +213,7 @@ func TestPrinterMultipleFilesInSequenceWithLeadingContent(t *testing.T) { func TestPrinterMultipleDocsInSinglePrint(t *testing.T) { var output bytes.Buffer var writer = bufio.NewWriter(&output) - printer := NewSimpleYamlPrinter(writer, YamlOutputFormat, true, false, 2, true) + printer := NewSimpleYamlPrinter(writer, true, false, 2, true) inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0, NewYamlDecoder(ConfiguredYamlPreferences)) if err != nil { @@ -232,7 +232,7 @@ func TestPrinterMultipleDocsInSinglePrint(t *testing.T) { func TestPrinterMultipleDocsInSinglePrintWithLeadingDoc(t *testing.T) { var output bytes.Buffer var writer = bufio.NewWriter(&output) - printer := NewSimpleYamlPrinter(writer, YamlOutputFormat, true, false, 2, true) + printer := NewSimpleYamlPrinter(writer, true, false, 2, true) inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0, NewYamlDecoder(ConfiguredYamlPreferences)) if err != nil { @@ -261,7 +261,7 @@ a: coconut func TestPrinterMultipleDocsInSinglePrintWithLeadingDocTrailing(t *testing.T) { var output bytes.Buffer var writer = bufio.NewWriter(&output) - printer := NewSimpleYamlPrinter(writer, YamlOutputFormat, true, false, 2, true) + printer := NewSimpleYamlPrinter(writer, true, false, 2, true) inputs, err := readDocuments(strings.NewReader(multiDocSample), "sample.yml", 0, NewYamlDecoder(ConfiguredYamlPreferences)) if err != nil { @@ -287,7 +287,7 @@ a: coconut func TestPrinterScalarWithLeadingCont(t *testing.T) { var output bytes.Buffer var writer = bufio.NewWriter(&output) - printer := NewSimpleYamlPrinter(writer, YamlOutputFormat, true, false, 2, true) + printer := NewSimpleYamlPrinter(writer, true, false, 2, true) node, err := getExpressionParser().ParseExpression(".a") if err != nil { @@ -344,7 +344,7 @@ func TestPrinterMultipleDocsJson(t *testing.T) { func TestPrinterNulSeparator(t *testing.T) { var output bytes.Buffer var writer = bufio.NewWriter(&output) - printer := NewSimpleYamlPrinter(writer, YamlOutputFormat, true, false, 2, false) + printer := NewSimpleYamlPrinter(writer, true, false, 2, false) printer.SetNulSepOutput(true) node, err := getExpressionParser().ParseExpression(".a") if err != nil { @@ -394,7 +394,7 @@ func TestPrinterNulSeparatorWithJson(t *testing.T) { func TestPrinterRootUnwrap(t *testing.T) { var output bytes.Buffer var writer = bufio.NewWriter(&output) - printer := NewSimpleYamlPrinter(writer, YamlOutputFormat, true, false, 2, false) + printer := NewSimpleYamlPrinter(writer, true, false, 2, false) node, err := getExpressionParser().ParseExpression(".") if err != nil { panic(err) diff --git a/pkg/yqlib/printer_writer.go b/pkg/yqlib/printer_writer.go index b900df27..2a72e1f6 100644 --- a/pkg/yqlib/printer_writer.go +++ b/pkg/yqlib/printer_writer.go @@ -33,7 +33,7 @@ type multiPrintWriter struct { index int } -func NewMultiPrinterWriter(expression *ExpressionNode, format PrinterOutputFormat) PrinterWriter { +func NewMultiPrinterWriter(expression *ExpressionNode, format *PrinterOutputFormat) PrinterWriter { extension := "yml" switch format {