diff --git a/_docs/mod/csv.md b/_docs/mod/csv.md index 8926ad1..3a43ae7 100644 --- a/_docs/mod/csv.md +++ b/_docs/mod/csv.md @@ -28,8 +28,10 @@ csv:each-record "winds.csv" { |row hdr| } ``` +### to-csv + ``` -csv:to-csv CONT [-header HEADER] +csv:to-csv CONT [-header HEADER] [-stdout] ``` Produces a CSV using the items of CONT and writes it as a string. CONT must be a list of iterator of hashable @@ -37,4 +39,6 @@ elements. If HEADER is defined, it must be a list of strings identifying the name and order of the hash keys to read from each hashable item of CONT. If HEADER is not defined, then the keys of the first consumed hashable item will be -used, with the keys sorted in alphabetical order. \ No newline at end of file +used, with the keys sorted in alphabetical order. + +If `-stdout` is set, the CSV will be written to standard out and `to-csv` will return the empty string. \ No newline at end of file diff --git a/ucl/builtins/csv.go b/ucl/builtins/csv.go index c013549..93fc664 100644 --- a/ucl/builtins/csv.go +++ b/ucl/builtins/csv.go @@ -144,7 +144,15 @@ func (h csvHandlers) toCsv(ctx context.Context, args ucl.CallArgs) (any, error) } var sb strings.Builder - cw := csv.NewWriter(&sb) + + var w io.Writer + if args.HasSwitch("stdout") { + w = os.Stdout + } else { + w = &sb + } + + cw := csv.NewWriter(w) i := 0 for { diff --git a/ucl/builtins/csv_test.go b/ucl/builtins/csv_test.go index e8a436e..e8c3132 100644 --- a/ucl/builtins/csv_test.go +++ b/ucl/builtins/csv_test.go @@ -53,6 +53,11 @@ func TestCSV_ReadRecord(t *testing.T) { } func TestCSV_ToCSV(t *testing.T) { + type testStruct struct { + Alpha string + Bravo string + } + tests := []struct { descr string eval string @@ -78,6 +83,16 @@ func TestCSV_ToCSV(t *testing.T) { eval: `itrs:from [[hello:"one" world:"this"] [hello:"two" other:"the" world:"that"]] | csv:to-csv -header [world other]`, want: "world,other\nthis,\nthat,the\n", }, + { + descr: "to csv 5", + eval: `test-native | csv:to-csv`, + want: "Alpha,Bravo\nHello,World\nAnother,Test\n", + }, + { + descr: "to csv 6", + eval: `test-native | itrs:from | csv:to-csv`, + want: "Alpha,Bravo\nHello,World\nAnother,Test\n", + }, } for _, tt := range tests { @@ -89,6 +104,12 @@ func TestCSV_ToCSV(t *testing.T) { ucl.WithModule(builtins.Itrs()), ucl.WithOut(&bfr), ) + inst.SetBuiltin("test-native", func(ctx context.Context, args ucl.CallArgs) (any, error) { + return []testStruct{ + {Alpha: "Hello", Bravo: "World"}, + {Alpha: "Another", Bravo: "Test"}, + }, nil + }) res, err := inst.Eval(context.Background(), tt.eval) assert.NoError(t, err)