From c4354d6a71b5dd0fe94527279d591c3cdb7651c3 Mon Sep 17 00:00:00 2001 From: Taichi Shigematsu <65220900+shigetaichi@users.noreply.github.com> Date: Wed, 16 Aug 2023 00:31:31 +0900 Subject: [PATCH] SelectedColumns (#6) * fix: README.md * feat: rename * tmp * feat: add test of selectedColumns * feat: add SelectedColumns --- README.md | 3 --- encode_test.go | 33 +++++++++++++++++++++++++++++++++ xsv_write.go | 36 +++++++++++++++++++++++++----------- xsv_writer.go | 5 ++--- 4 files changed, 60 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 044d008..ac2d42c 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,6 @@ Most of the programs related to csv generation and reading are created from code ※xsv does not include gocsv. -## 🚧Under Construction -there is a lot of things to do before release. - ## Getting Started ``` diff --git a/encode_test.go b/encode_test.go index bdca0c4..b6f0367 100644 --- a/encode_test.go +++ b/encode_test.go @@ -607,3 +607,36 @@ func Test_writeTo_nested_struct(t *testing.T) { assertLine(t, []string{"one.boolField1", "one.stringField2", "two.boolField1", "two.stringField2", "three.boolField1", "three.stringField2"}, lines[0]) assertLine(t, []string{"false", "email_one", "true", "email_two", "false", "email_three"}, lines[1]) } + +func Test_writeTo_emptyptr_selectedColumns(t *testing.T) { + + b := bytes.Buffer{} + e := &encoder{out: &b} + blah := 2 + sptr := "*string" + s := []EmbedPtrSample{ + { + Qux: "aaa", + Sample: &Sample{Foo: "f", Bar: 1, Baz: "baz", Frop: 0.2, Blah: &blah, SPtr: &sptr}, + Ignore: "shouldn't be marshalled", + Quux: "zzz", + Grault: math.Pi, + }, + } + + xsvWrite := NewXsvWrite[EmbedPtrSample]() + xsvWrite.SelectedColumns = []string{"first", "-", "Baz", "last"} // If a "-" exists here, it will not be output. + if err := xsvWrite.SetWriter(csv.NewWriter(e.out)).Write(s); err != nil { + t.Fatal(err) + } + + lines, err := csv.NewReader(&b).ReadAll() + if err != nil { + t.Fatal(err) + } + if len(lines) != 2 { + t.Fatalf("expected 2 lines, got %d", len(lines)) + } + assertLine(t, []string{"first", "Baz", "last"}, lines[0]) + assertLine(t, []string{"aaa", "baz", "zzz"}, lines[1]) +} diff --git a/xsv_write.go b/xsv_write.go index df499c5..5d8bf12 100644 --- a/xsv_write.go +++ b/xsv_write.go @@ -4,23 +4,25 @@ import ( "bytes" "encoding/csv" "os" + "slices" ) type XsvWrite[T any] struct { - TagName string //key in the struct field's tag to scan - TagSeparator string //separator string for multiple csv tags in struct fields - OmitHeaders bool - selectedColumnIndex []int // TODO: describe in comment - columnSorter ColumnSorter // TODO: describe in comment - nameNormalizer Normalizer + TagName string //key in the struct field's tag to scan + TagSeparator string //separator string for multiple csv tags in struct fields + OmitHeaders bool + SelectedColumns []string // slice of field names to output + columnSorter ColumnSorter // TODO: describe in comment + nameNormalizer Normalizer } +type ColumnSorter = func(row []string) []string func NewXsvWrite[T any]() XsvWrite[T] { return XsvWrite[T]{ - TagName: "csv", - TagSeparator: ",", - OmitHeaders: false, - selectedColumnIndex: make([]int, 0), + TagName: "csv", + TagSeparator: ",", + OmitHeaders: false, + SelectedColumns: make([]string, 0), columnSorter: func(row []string) []string { return row }, @@ -28,7 +30,19 @@ func NewXsvWrite[T any]() XsvWrite[T] { } } -type ColumnSorter = func(row []string) []string +func (x *XsvWrite[T]) getSelectedFieldInfos(fieldInfos []fieldInfo) []fieldInfo { + if len(x.SelectedColumns) > 0 { + var selectedFieldInfos []fieldInfo + for _, info := range fieldInfos { + if slices.Index(x.SelectedColumns, info.keys[0]) >= 0 { + selectedFieldInfos = append(selectedFieldInfos, info) + } + } + return selectedFieldInfos + } else { + return fieldInfos + } +} func (x *XsvWrite[T]) SetWriter(writer *csv.Writer) (xw *XsvWriter[T]) { xw = NewXsvWriter(*x) diff --git a/xsv_writer.go b/xsv_writer.go index e88f219..e8842fa 100644 --- a/xsv_writer.go +++ b/xsv_writer.go @@ -33,10 +33,9 @@ func (xw *XsvWriter[T]) Write(data []T) error { } fieldInfos := getFieldInfos(inInnerType, []int{}, []string{}, xw.TagName, xw.TagSeparator, xw.nameNormalizer) // Get the inner struct info to get CSV annotations + fieldInfos = xw.getSelectedFieldInfos(fieldInfos) inInnerStructInfo := &structInfo{fieldInfos} - inInnerStructInfo.Fields = getPickedFields(inInnerStructInfo.Fields, xw.selectedColumnIndex) // Filter Fields from all fields - csvHeadersLabels := make([]string, len(inInnerStructInfo.Fields)) for i, fieldInfo := range inInnerStructInfo.Fields { // Used to write the header (first line) in CSV csvHeadersLabels[i] = fieldInfo.getFirstKey() @@ -78,8 +77,8 @@ func (xw *XsvWriter[T]) WriteFromChan(dataChan chan T) error { } inInnerWasPointer := inType.Kind() == reflect.Ptr fieldInfos := getFieldInfos(inType, []int{}, []string{}, xw.TagName, xw.TagSeparator, xw.nameNormalizer) // Get the inner struct info to get CSV annotations + fieldInfos = xw.getSelectedFieldInfos(fieldInfos) inInnerStructInfo := &structInfo{fieldInfos} - inInnerStructInfo.Fields = getPickedFields(inInnerStructInfo.Fields, xw.selectedColumnIndex) // Filtered out ignoreFields from all fields csvHeadersLabels := make([]string, len(inInnerStructInfo.Fields)) for i, fieldInfo := range inInnerStructInfo.Fields { // Used to Write the header (first line) in CSV csvHeadersLabels[i] = fieldInfo.getFirstKey()