Skip to content

Commit 53deda7

Browse files
authored
feat: allow struct slice and struct array for valueType (#70)
* feat: allow struct slice and struct array for to determine Header
1 parent 4c3b8e6 commit 53deda7

File tree

3 files changed

+108
-9
lines changed

3 files changed

+108
-9
lines changed

csvutil.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ func countRecords(s []byte) (n int) {
154154
}
155155
}
156156

157-
// Header scans the provided struct type and generates a CSV header for it.
157+
// Header scans the provided struct type, struct slice or struct array and generates a CSV header for it.
158158
//
159159
// Field names are written in the same order as struct fields are defined.
160160
// Embedded struct's fields are treated as if they were part of the outer struct.
@@ -175,8 +175,8 @@ func countRecords(s []byte) (n int) {
175175
//
176176
// If tag is left empty the default "csv" will be used.
177177
//
178-
// Header will return UnsupportedTypeError if the provided value is nil or is
179-
// not a struct.
178+
// Header will return UnsupportedTypeError if the provided value is nil, is
179+
// not a struct, a struct slice or a struct array.
180180
func Header(v any, tag string) ([]string, error) {
181181
typ, err := valueType(v)
182182
if err != nil {
@@ -216,10 +216,15 @@ loop:
216216
}
217217

218218
typ := walkType(val.Type())
219-
if typ.Kind() != reflect.Struct {
220-
return nil, &UnsupportedTypeError{Type: typ}
219+
switch typ.Kind() {
220+
case reflect.Struct:
221+
return typ, nil
222+
case reflect.Slice, reflect.Array:
223+
if eTyp := walkType(typ.Elem()); eTyp.Kind() == reflect.Struct {
224+
return eTyp, nil
225+
}
221226
}
222-
return typ, nil
227+
return nil, &UnsupportedTypeError{Type: typ}
223228
}
224229

225230
func newCSVReader(r io.Reader) *csv.Reader {

csvutil_test.go

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -627,10 +627,64 @@ func TestHeader(t *testing.T) {
627627
err: &UnsupportedTypeError{Type: reflect.TypeOf(int(0))},
628628
},
629629
{
630-
desc: "slice",
631-
v: []TypeJ{{}},
630+
desc: "slice",
631+
v: []TypeJ{{}},
632+
tag: "csv",
633+
header: []string{"STR", "int", "Bool", "Uint8", "float"},
634+
},
635+
{
636+
desc: "ptr slice",
637+
v: &[]TypeJ{{}},
638+
tag: "csv",
639+
header: []string{"STR", "int", "Bool", "Uint8", "float"},
640+
},
641+
{
642+
desc: "slice with ptr value",
643+
v: []*TypeJ{{}},
644+
tag: "csv",
645+
header: []string{"STR", "int", "Bool", "Uint8", "float"},
646+
},
647+
{
648+
desc: "slice with non-struct",
649+
v: []int{0},
650+
tag: "csv",
651+
err: &UnsupportedTypeError{Type: reflect.TypeOf([]int{0})},
652+
},
653+
{
654+
desc: "two-dimensional slice",
655+
v: [][]TypeJ{{{}}},
656+
tag: "csv",
657+
err: &UnsupportedTypeError{Type: reflect.TypeOf([][]TypeJ{{}})},
658+
},
659+
{
660+
desc: "array",
661+
v: [1]TypeJ{{}},
662+
tag: "csv",
663+
header: []string{"STR", "int", "Bool", "Uint8", "float"},
664+
},
665+
{
666+
desc: "ptr array",
667+
v: &[1]TypeJ{{}},
668+
tag: "csv",
669+
header: []string{"STR", "int", "Bool", "Uint8", "float"},
670+
},
671+
{
672+
desc: "array with ptr value",
673+
v: [1]*TypeJ{{}},
674+
tag: "csv",
675+
header: []string{"STR", "int", "Bool", "Uint8", "float"},
676+
},
677+
{
678+
desc: "array with non-struct",
679+
v: [1]int{0},
680+
tag: "csv",
681+
err: &UnsupportedTypeError{Type: reflect.TypeOf([1]int{0})},
682+
},
683+
{
684+
desc: "two-dimensional array",
685+
v: [1][1]TypeJ{{{}}},
632686
tag: "csv",
633-
err: &UnsupportedTypeError{Type: reflect.TypeOf([]TypeJ{})},
687+
err: &UnsupportedTypeError{Type: reflect.TypeOf([1][1]TypeJ{{}})},
634688
},
635689
{
636690
desc: "nil interface",

encoder_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1778,6 +1778,46 @@ func TestEncoder(t *testing.T) {
17781778
},
17791779
},
17801780
},
1781+
{
1782+
desc: "struct slice",
1783+
in: []TypeF{},
1784+
out: [][]string{
1785+
{
1786+
"int",
1787+
"pint",
1788+
"int8",
1789+
"pint8",
1790+
"int16",
1791+
"pint16",
1792+
"int32",
1793+
"pint32",
1794+
"int64",
1795+
"pint64",
1796+
"uint",
1797+
"puint",
1798+
"uint8",
1799+
"puint8",
1800+
"uint16",
1801+
"puint16",
1802+
"uint32",
1803+
"puint32",
1804+
"uint64",
1805+
"puint64",
1806+
"float32",
1807+
"pfloat32",
1808+
"float64",
1809+
"pfloat64",
1810+
"string",
1811+
"pstring",
1812+
"bool",
1813+
"pbool",
1814+
"interface",
1815+
"pinterface",
1816+
"binary",
1817+
"pbinary",
1818+
},
1819+
},
1820+
},
17811821
{
17821822
desc: "ptr to nil interface",
17831823
in: &nilIface,

0 commit comments

Comments
 (0)