Added csv separator flag #1950

This commit is contained in:
Mike Farah 2024-02-17 19:10:13 +11:00
parent 42439b7d00
commit 9a8151d316
7 changed files with 89 additions and 12 deletions

View File

@ -63,6 +63,27 @@ EOM
assertEquals "$expected" "$X" assertEquals "$expected" "$X"
} }
testInputCSVCustomSeparator() {
cat >test.csv <<EOL
fruit;yumLevel
apple;5
banana;4
EOL
read -r -d '' expected << EOM
- fruit: apple
yumLevel: 5
- fruit: banana
yumLevel: 4
EOM
X=$(./yq -p=csv --csv-separator ";" test.csv)
assertEquals "$expected" "$X"
X=$(./yq ea -p=csv --csv-separator ";" test.csv)
assertEquals "$expected" "$X"
}
testInputCSVNoAuto() { testInputCSVNoAuto() {
cat >test.csv <<EOL cat >test.csv <<EOL
thing1 thing1

View File

@ -198,6 +198,27 @@ EOM
assertEquals "$expected" "$X" assertEquals "$expected" "$X"
} }
testOutputCSVCustomSeparator() {
cat >test.yml <<EOL
- fruit: apple
yumLevel: 5
- fruit: banana
yumLevel: 4
EOL
read -r -d '' expected << EOM
fruit;yumLevel
apple;5
banana;4
EOM
X=$(./yq -oc --csv-separator ";" test.yml)
assertEquals "$expected" "$X"
X=$(./yq ea -o=csv --csv-separator ";" test.yml)
assertEquals "$expected" "$X"
}
testOutputTSV() { testOutputTSV() {
cat >test.yml <<EOL cat >test.yml <<EOL
- fruit: apple - fruit: apple

View File

@ -1,13 +1,42 @@
package cmd package cmd
import ( import (
"fmt"
"os" "os"
"strings"
"github.com/mikefarah/yq/v4/pkg/yqlib" "github.com/mikefarah/yq/v4/pkg/yqlib"
"github.com/spf13/cobra" "github.com/spf13/cobra"
logging "gopkg.in/op/go-logging.v1" logging "gopkg.in/op/go-logging.v1"
) )
type runeValue rune
func newRuneVar(p *rune) *runeValue {
return (*runeValue)(p)
}
func (r *runeValue) String() string {
return string(*r)
}
func (r *runeValue) Set(rawVal string) error {
val := strings.ReplaceAll(rawVal, "\\n", "\n")
val = strings.ReplaceAll(val, "\\t", "\t")
val = strings.ReplaceAll(val, "\\r", "\r")
val = strings.ReplaceAll(val, "\\f", "\f")
val = strings.ReplaceAll(val, "\\v", "\v")
if len(val) != 1 {
return fmt.Errorf("[%v] is not a valid character. Must be length 1 was %v", val, len(val))
}
*r = runeValue(rune(val[0]))
return nil
}
func (r *runeValue) Type() string {
return "char"
}
func New() *cobra.Command { func New() *cobra.Command {
var rootCmd = &cobra.Command{ var rootCmd = &cobra.Command{
Use: "yq", Use: "yq",
@ -82,6 +111,8 @@ yq -P -oy sample.json
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredXMLPreferences.SkipDirectives, "xml-skip-directives", yqlib.ConfiguredXMLPreferences.SkipDirectives, "skip over directives (e.g. <!DOCTYPE thing cat>)") rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredXMLPreferences.SkipDirectives, "xml-skip-directives", yqlib.ConfiguredXMLPreferences.SkipDirectives, "skip over directives (e.g. <!DOCTYPE thing cat>)")
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredCsvPreferences.AutoParse, "csv-auto-parse", yqlib.ConfiguredCsvPreferences.AutoParse, "parse CSV YAML/JSON values") rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredCsvPreferences.AutoParse, "csv-auto-parse", yqlib.ConfiguredCsvPreferences.AutoParse, "parse CSV YAML/JSON values")
rootCmd.PersistentFlags().Var(newRuneVar(&yqlib.ConfiguredCsvPreferences.Separator), "csv-separator", "CSV Separator character")
rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredTsvPreferences.AutoParse, "tsv-auto-parse", yqlib.ConfiguredTsvPreferences.AutoParse, "parse TSV YAML/JSON values") rootCmd.PersistentFlags().BoolVar(&yqlib.ConfiguredTsvPreferences.AutoParse, "tsv-auto-parse", yqlib.ConfiguredTsvPreferences.AutoParse, "parse TSV YAML/JSON values")
rootCmd.PersistentFlags().StringVar(&yqlib.ConfiguredLuaPreferences.DocPrefix, "lua-prefix", yqlib.ConfiguredLuaPreferences.DocPrefix, "prefix") rootCmd.PersistentFlags().StringVar(&yqlib.ConfiguredLuaPreferences.DocPrefix, "lua-prefix", yqlib.ConfiguredLuaPreferences.DocPrefix, "prefix")

View File

@ -189,9 +189,9 @@ func createEncoder(format yqlib.PrinterOutputFormat) (yqlib.Encoder, error) {
case yqlib.PropsOutputFormat: case yqlib.PropsOutputFormat:
return yqlib.NewPropertiesEncoder(unwrapScalar), nil return yqlib.NewPropertiesEncoder(unwrapScalar), nil
case yqlib.CSVOutputFormat: case yqlib.CSVOutputFormat:
return yqlib.NewCsvEncoder(','), nil return yqlib.NewCsvEncoder(yqlib.ConfiguredCsvPreferences), nil
case yqlib.TSVOutputFormat: case yqlib.TSVOutputFormat:
return yqlib.NewCsvEncoder('\t'), nil return yqlib.NewCsvEncoder(yqlib.ConfiguredTsvPreferences), nil
case yqlib.YamlOutputFormat: case yqlib.YamlOutputFormat:
return yqlib.NewYamlEncoder(indent, colorsEnabled, yqlib.ConfiguredYamlPreferences), nil return yqlib.NewYamlEncoder(indent, colorsEnabled, yqlib.ConfiguredYamlPreferences), nil
case yqlib.XMLOutputFormat: case yqlib.XMLOutputFormat:

View File

@ -206,9 +206,9 @@ var csvScenarios = []formatScenario{
func testCSVScenario(t *testing.T, s formatScenario) { func testCSVScenario(t *testing.T, s formatScenario) {
switch s.scenarioType { switch s.scenarioType {
case "encode-csv": case "encode-csv":
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewCsvEncoder(',')), s.description) test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewCsvEncoder(ConfiguredCsvPreferences)), s.description)
case "encode-tsv": case "encode-tsv":
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewCsvEncoder('\t')), s.description) test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewCsvEncoder(ConfiguredTsvPreferences)), s.description)
case "decode-csv": case "decode-csv":
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewCSVObjectDecoder(ConfiguredCsvPreferences), NewYamlEncoder(2, false, ConfiguredYamlPreferences)), s.description) test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewCSVObjectDecoder(ConfiguredCsvPreferences), NewYamlEncoder(2, false, ConfiguredYamlPreferences)), s.description)
case "decode-csv-no-auto": case "decode-csv-no-auto":
@ -216,7 +216,7 @@ func testCSVScenario(t *testing.T, s formatScenario) {
case "decode-tsv-object": case "decode-tsv-object":
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewCSVObjectDecoder(ConfiguredTsvPreferences), NewYamlEncoder(2, false, ConfiguredYamlPreferences)), s.description) test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewCSVObjectDecoder(ConfiguredTsvPreferences), NewYamlEncoder(2, false, ConfiguredYamlPreferences)), s.description)
case "roundtrip-csv": case "roundtrip-csv":
test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewCSVObjectDecoder(ConfiguredCsvPreferences), NewCsvEncoder(',')), s.description) test.AssertResultWithContext(t, s.expected, mustProcessFormatScenario(s, NewCSVObjectDecoder(ConfiguredCsvPreferences), NewCsvEncoder(ConfiguredCsvPreferences)), s.description)
default: default:
panic(fmt.Sprintf("unhandled scenario type %q", s.scenarioType)) panic(fmt.Sprintf("unhandled scenario type %q", s.scenarioType))
} }
@ -298,9 +298,10 @@ func documentCSVEncodeScenario(w *bufio.Writer, s formatScenario, formatType str
if formatType == "tsv" { if formatType == "tsv" {
separator = '\t' separator = '\t'
} }
csvPrefs := NewDefaultCsvPreferences()
csvPrefs.Separator = separator
writeOrPanic(w, fmt.Sprintf("```%v\n%v```\n\n", formatType, writeOrPanic(w, fmt.Sprintf("```%v\n%v```\n\n", formatType,
mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewCsvEncoder(separator))), mustProcessFormatScenario(s, NewYamlDecoder(ConfiguredYamlPreferences), NewCsvEncoder(csvPrefs))),
) )
} }
@ -331,8 +332,11 @@ func documentCSVRoundTripScenario(w *bufio.Writer, s formatScenario, formatType
separator = '\t' separator = '\t'
} }
csvPrefs := NewDefaultCsvPreferences()
csvPrefs.Separator = separator
writeOrPanic(w, fmt.Sprintf("```%v\n%v```\n\n", formatType, writeOrPanic(w, fmt.Sprintf("```%v\n%v```\n\n", formatType,
mustProcessFormatScenario(s, NewCSVObjectDecoder(CsvPreferences{Separator: separator, AutoParse: true}), NewCsvEncoder(separator))), mustProcessFormatScenario(s, NewCSVObjectDecoder(CsvPreferences{Separator: separator, AutoParse: true}), NewCsvEncoder(csvPrefs))),
) )
} }

View File

@ -10,8 +10,8 @@ type csvEncoder struct {
separator rune separator rune
} }
func NewCsvEncoder(separator rune) Encoder { func NewCsvEncoder(prefs CsvPreferences) Encoder {
return &csvEncoder{separator: separator} return &csvEncoder{separator: prefs.Separator}
} }
func (e *csvEncoder) CanHandleAliases() bool { func (e *csvEncoder) CanHandleAliases() bool {

View File

@ -16,9 +16,9 @@ func configureEncoder(format PrinterOutputFormat, indent int) Encoder {
case PropsOutputFormat: case PropsOutputFormat:
return NewPropertiesEncoder(true) return NewPropertiesEncoder(true)
case CSVOutputFormat: case CSVOutputFormat:
return NewCsvEncoder(',') return NewCsvEncoder(ConfiguredCsvPreferences)
case TSVOutputFormat: case TSVOutputFormat:
return NewCsvEncoder('\t') return NewCsvEncoder(ConfiguredTsvPreferences)
case YamlOutputFormat: case YamlOutputFormat:
return NewYamlEncoder(indent, false, ConfiguredYamlPreferences) return NewYamlEncoder(indent, false, ConfiguredYamlPreferences)
case XMLOutputFormat: case XMLOutputFormat: