diff --git a/cmd/utils.go b/cmd/utils.go index de147954..e54cfb79 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -75,9 +75,6 @@ func initCommand(cmd *cobra.Command, args []string) (string, []string, error) { } } else if isAutomaticOutputFormat() { // automatic input worked, we can do it for output too unless specified - if inputFormat == "toml" { - return "", nil, fmt.Errorf("toml is not yet supported as an output format. Please specify another output format using the [--output-format/-o] flag") - } if inputFormat == "json" { yqlib.GetLogger().Warning("JSON file output is now JSON by default (instead of yaml). Use '-oy' or '--output-format=yaml' for yaml output") } @@ -196,6 +193,8 @@ func createEncoder(format yqlib.PrinterOutputFormat) (yqlib.Encoder, error) { return yqlib.NewYamlEncoder(indent, colorsEnabled, yqlib.ConfiguredYamlPreferences), nil case yqlib.XMLOutputFormat: return yqlib.NewXMLEncoder(indent, yqlib.ConfiguredXMLPreferences), nil + case yqlib.TomlOutputFormat: + return yqlib.NewTomlEncoder(), nil } return nil, fmt.Errorf("invalid encoder: %v", format) } diff --git a/pkg/yqlib/doc/usage/toml.md b/pkg/yqlib/doc/usage/toml.md index 1ebe2336..65ed5348 100644 --- a/pkg/yqlib/doc/usage/toml.md +++ b/pkg/yqlib/doc/usage/toml.md @@ -38,6 +38,22 @@ person: address: 12 cat st ``` +## Encode: Scalar +Given a sample.toml file of: +```toml +person.name = "hello" +person.address = "12 cat st" + +``` +then +```bash +yq '.person.name' sample.toml +``` +will output +```yaml +hello +``` + ## Parse: inline table Given a sample.toml file of: ```toml diff --git a/pkg/yqlib/encoder_toml.go b/pkg/yqlib/encoder_toml.go new file mode 100644 index 00000000..13b4e1e3 --- /dev/null +++ b/pkg/yqlib/encoder_toml.go @@ -0,0 +1,34 @@ +package yqlib + +import ( + "fmt" + "io" + + yaml "gopkg.in/yaml.v3" +) + +type tomlEncoder struct { +} + +func NewTomlEncoder() Encoder { + return &tomlEncoder{} +} + +func (te *tomlEncoder) Encode(writer io.Writer, node *yaml.Node) error { + if node.Kind == yaml.ScalarNode { + return writeString(writer, node.Value+"\n") + } + return fmt.Errorf("only scalars (e.g. strings, numbers, booleans) are supported for TOML output at the moment. Please use yaml output format (-oy) until the encoder has been fully implemented") +} + +func (te *tomlEncoder) PrintDocumentSeparator(writer io.Writer) error { + return nil +} + +func (te *tomlEncoder) PrintLeadingContent(writer io.Writer, content string) error { + return nil +} + +func (te *tomlEncoder) CanHandleAliases() bool { + return false +} diff --git a/pkg/yqlib/printer.go b/pkg/yqlib/printer.go index 1bef5631..a2d9f4ed 100644 --- a/pkg/yqlib/printer.go +++ b/pkg/yqlib/printer.go @@ -31,6 +31,7 @@ const ( Base64OutputFormat UriOutputFormat ShOutputFormat + TomlOutputFormat ) func OutputFormatFromString(format string) (PrinterOutputFormat, error) { @@ -47,6 +48,8 @@ func OutputFormatFromString(format string) (PrinterOutputFormat, error) { return TSVOutputFormat, nil case "xml", "x": return XMLOutputFormat, nil + case "toml": + return TomlOutputFormat, nil default: return 0, fmt.Errorf("unknown format '%v' please use [yaml|json|props|csv|tsv|xml]", format) } diff --git a/pkg/yqlib/toml_test.go b/pkg/yqlib/toml_test.go index 1b29c4f0..f55d2fdf 100644 --- a/pkg/yqlib/toml_test.go +++ b/pkg/yqlib/toml_test.go @@ -94,6 +94,13 @@ var tomlScenarios = []formatScenario{ expected: "person:\n name: hello\n address: 12 cat st\n", scenarioType: "decode", }, + { + description: "Encode: Scalar", + input: "person.name = \"hello\"\nperson.address = \"12 cat st\"\n", + expression: ".person.name", + expected: "hello\n", + scenarioType: "roundtrip", + }, { skipDoc: true, input: `A.B = "hello"`, @@ -188,6 +195,8 @@ func testTomlScenario(t *testing.T, s formatScenario) { } else { test.AssertResultComplexWithContext(t, s.expectedError, err.Error(), s.description) } + case "roundtrip": + test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewTomlDecoder(), NewTomlEncoder()), s.description) } } @@ -213,6 +222,28 @@ func documentTomlDecodeScenario(w *bufio.Writer, s formatScenario) { writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", mustProcessFormatScenario(s, NewTomlDecoder(), NewYamlEncoder(2, false, ConfiguredYamlPreferences)))) } +func documentTomlRoundtripScenario(w *bufio.Writer, s formatScenario) { + writeOrPanic(w, fmt.Sprintf("## %v\n", s.description)) + + if s.subdescription != "" { + writeOrPanic(w, s.subdescription) + writeOrPanic(w, "\n\n") + } + + writeOrPanic(w, "Given a sample.toml file of:\n") + writeOrPanic(w, fmt.Sprintf("```toml\n%v\n```\n", s.input)) + + writeOrPanic(w, "then\n") + expression := s.expression + if expression == "" { + expression = "." + } + writeOrPanic(w, fmt.Sprintf("```bash\nyq '%v' sample.toml\n```\n", expression)) + writeOrPanic(w, "will output\n") + + writeOrPanic(w, fmt.Sprintf("```yaml\n%v```\n\n", mustProcessFormatScenario(s, NewTomlDecoder(), NewTomlEncoder()))) +} + func documentTomlScenario(t *testing.T, w *bufio.Writer, i interface{}) { s := i.(formatScenario) @@ -222,6 +253,8 @@ func documentTomlScenario(t *testing.T, w *bufio.Writer, i interface{}) { switch s.scenarioType { case "", "decode": documentTomlDecodeScenario(w, s) + case "roundtrip": + documentTomlRoundtripScenario(w, s) default: panic(fmt.Sprintf("unhandled scenario type %q", s.scenarioType))