1package csvutil
2
3import (
4	"bytes"
5	"encoding"
6	"encoding/csv"
7	"encoding/json"
8	"errors"
9	"math"
10	"reflect"
11	"testing"
12)
13
14var Error = errors.New("error")
15
16var nilIface interface{}
17
18var nilPtr *TypeF
19
20var nilIfacePtr interface{} = nilPtr
21
22type embeddedMap map[string]string
23
24type Embedded14 Embedded3
25
26func (e *Embedded14) MarshalCSV() ([]byte, error) {
27	return json.Marshal(e)
28}
29
30type Embedded15 Embedded3
31
32func (e *Embedded15) MarshalText() ([]byte, error) {
33	return json.Marshal(Embedded3(*e))
34}
35
36type CSVMarshaler struct {
37	Err error
38}
39
40func (m CSVMarshaler) MarshalCSV() ([]byte, error) {
41	if m.Err != nil {
42		return nil, m.Err
43	}
44	return []byte("csvmarshaler"), nil
45}
46
47type PtrRecCSVMarshaler int
48
49func (m *PtrRecCSVMarshaler) MarshalCSV() ([]byte, error) {
50	return []byte("ptrreccsvmarshaler"), nil
51}
52
53func (m *PtrRecCSVMarshaler) CSV() ([]byte, error) {
54	return []byte("ptrreccsvmarshaler.CSV"), nil
55}
56
57type PtrRecTextMarshaler int
58
59func (m *PtrRecTextMarshaler) MarshalText() ([]byte, error) {
60	return []byte("ptrrectextmarshaler"), nil
61}
62
63type TextMarshaler struct {
64	Err error
65}
66
67func (m TextMarshaler) MarshalText() ([]byte, error) {
68	if m.Err != nil {
69		return nil, m.Err
70	}
71	return []byte("textmarshaler"), nil
72}
73
74type CSVTextMarshaler struct {
75	CSVMarshaler
76	TextMarshaler
77}
78
79type Inline struct {
80	J1      TypeJ  `csv:",inline"`
81	J2      TypeJ  `csv:"prefix-,inline"`
82	String  string `csv:"top-string"`
83	String2 string `csv:"STR"`
84}
85
86type Inline2 struct {
87	S string
88	A Inline3 `csv:"A,inline"`
89	B Inline3 `csv:",inline"`
90}
91
92type Inline3 struct {
93	Inline4 `csv:",inline"`
94}
95
96type Inline4 struct {
97	A string
98}
99
100type Inline5 struct {
101	A Inline2 `csv:"A,inline"`
102	B Inline2 `csv:",inline"`
103}
104
105type Inline6 struct {
106	A Inline7 `csv:",inline"`
107}
108
109type Inline7 struct {
110	A *Inline6 `csv:",inline"`
111	X int
112}
113
114type Inline8 struct {
115	F  *Inline4 `csv:"A,inline"`
116	AA int
117}
118
119type TypeH struct {
120	Int     int         `csv:"int,omitempty"`
121	Int8    int8        `csv:"int8,omitempty"`
122	Int16   int16       `csv:"int16,omitempty"`
123	Int32   int32       `csv:"int32,omitempty"`
124	Int64   int64       `csv:"int64,omitempty"`
125	UInt    uint        `csv:"uint,omitempty"`
126	Uint8   uint8       `csv:"uint8,omitempty"`
127	Uint16  uint16      `csv:"uint16,omitempty"`
128	Uint32  uint32      `csv:"uint32,omitempty"`
129	Uint64  uint64      `csv:"uint64,omitempty"`
130	Float32 float32     `csv:"float32,omitempty"`
131	Float64 float64     `csv:"float64,omitempty"`
132	String  string      `csv:"string,omitempty"`
133	Bool    bool        `csv:"bool,omitempty"`
134	V       interface{} `csv:"interface,omitempty"`
135}
136
137type TypeM struct {
138	*TextMarshaler `csv:"text"`
139}
140
141func TestEncoder(t *testing.T) {
142	fixtures := []struct {
143		desc    string
144		in      []interface{}
145		regFunc []interface{}
146		out     [][]string
147		err     error
148	}{
149		{
150			desc: "test all types",
151			in: []interface{}{
152				TypeF{
153					Int:      1,
154					Pint:     pint(2),
155					Int8:     3,
156					Pint8:    pint8(4),
157					Int16:    5,
158					Pint16:   pint16(6),
159					Int32:    7,
160					Pint32:   pint32(8),
161					Int64:    9,
162					Pint64:   pint64(10),
163					UInt:     11,
164					Puint:    puint(12),
165					Uint8:    13,
166					Puint8:   puint8(14),
167					Uint16:   15,
168					Puint16:  puint16(16),
169					Uint32:   17,
170					Puint32:  puint32(18),
171					Uint64:   19,
172					Puint64:  puint64(20),
173					Float32:  21,
174					Pfloat32: pfloat32(22),
175					Float64:  23,
176					Pfloat64: pfloat64(24),
177					String:   "25",
178					PString:  pstring("26"),
179					Bool:     true,
180					Pbool:    pbool(true),
181					V:        "true",
182					Pv:       pinterface("1"),
183					Binary:   Binary,
184					PBinary:  &BinaryLarge,
185				},
186				TypeF{},
187			},
188			out: [][]string{
189				{
190					"int", "pint", "int8", "pint8", "int16", "pint16", "int32",
191					"pint32", "int64", "pint64", "uint", "puint", "uint8", "puint8",
192					"uint16", "puint16", "uint32", "puint32", "uint64", "puint64",
193					"float32", "pfloat32", "float64", "pfloat64", "string", "pstring",
194					"bool", "pbool", "interface", "pinterface", "binary", "pbinary",
195				},
196				{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11",
197					"12", "13", "14", "15", "16", "17", "18", "19", "20", "21",
198					"22", "23", "24", "25", "26", "true", "true", "true", "1",
199					EncodedBinary, EncodedBinaryLarge,
200				},
201				{"0", "", "0", "", "0", "", "0", "", "0", "", "0", "",
202					"0", "", "0", "", "0", "", "0", "", "0", "", "0", "", "", "",
203					"false", "", "", "", "", "",
204				},
205			},
206		},
207		{
208			desc: "tags and unexported fields",
209			in: []interface{}{
210				TypeG{
211					String:      "string",
212					Int:         1,
213					Float:       3.14,
214					unexported1: 100,
215					unexported2: 200,
216				},
217			},
218			out: [][]string{
219				{"String", "Int"},
220				{"string", "1"},
221			},
222		},
223		{
224			desc: "omitempty tags",
225			in: []interface{}{
226				TypeH{},
227			},
228			out: [][]string{
229				{"int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16",
230					"uint32", "uint64", "float32", "float64", "string", "bool", "interface",
231				},
232				{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""},
233			},
234		},
235		{
236			desc: "omitempty tags on pointers - non nil default values",
237			in: []interface{}{
238				struct {
239					Pint    *int         `csv:",omitempty"`
240					PPint   **int        `csv:",omitempty"`
241					PPint2  **int        `csv:",omitempty"`
242					PString *string      `csv:",omitempty"`
243					PBool   *bool        `csv:",omitempty"`
244					Iint    *interface{} `csv:",omitempty"`
245				}{
246					pint(0),
247					ppint(0),
248					new(*int),
249					pstring(""),
250					pbool(false),
251					pinterface(0),
252				},
253			},
254			out: [][]string{
255				{"Pint", "PPint", "PPint2", "PString", "PBool", "Iint"},
256				{"0", "0", "", "", "false", "0"},
257			},
258		},
259		{
260			desc: "omitempty tags on pointers - nil ptrs",
261			in: []interface{}{
262				struct {
263					Pint    *int         `csv:",omitempty"`
264					PPint   **int        `csv:",omitempty"`
265					PString *string      `csv:",omitempty"`
266					PBool   *bool        `csv:",omitempty"`
267					Iint    *interface{} `csv:",omitempty"`
268				}{},
269			},
270			out: [][]string{
271				{"Pint", "PPint", "PString", "PBool", "Iint"},
272				{"", "", "", "", ""},
273			},
274		},
275		{
276			desc: "omitempty tags on interfaces - non nil default values",
277			in: []interface{}{
278				struct {
279					Iint  interface{} `csv:",omitempty"`
280					IPint interface{} `csv:",omitempty"`
281				}{
282					0,
283					pint(0),
284				},
285				struct {
286					Iint  interface{} `csv:",omitempty"`
287					IPint interface{} `csv:",omitempty"`
288				}{
289					1,
290					pint(1),
291				},
292			},
293			out: [][]string{
294				{"Iint", "IPint"},
295				{"0", "0"},
296				{"1", "1"},
297			},
298		},
299		{
300			desc: "omitempty tags on interfaces - nil",
301			in: []interface{}{
302				struct {
303					Iint  interface{} `csv:",omitempty"`
304					IPint interface{} `csv:",omitempty"`
305				}{
306					nil,
307					nil,
308				},
309				struct {
310					Iint  interface{} `csv:",omitempty"`
311					IPint interface{} `csv:",omitempty"`
312				}{
313					(*int)(nil),
314					pinterface((*int)(nil)),
315				},
316			},
317			out: [][]string{
318				{"Iint", "IPint"},
319				{"", ""},
320				{"", ""},
321			},
322		},
323		{
324			desc: "embedded types #1",
325			in: []interface{}{
326				TypeA{
327					Embedded1: Embedded1{
328						String: "string1",
329						Float:  1,
330					},
331					String: "string",
332					Embedded2: Embedded2{
333						Float: 2,
334						Bool:  true,
335					},
336					Int: 10,
337				},
338			},
339			out: [][]string{
340				{"string", "bool", "int"},
341				{"string", "true", "10"},
342			},
343		},
344		{
345			desc: "embedded non struct tagged types",
346			in: []interface{}{
347				TypeB{
348					Embedded3: Embedded3{"key": "val"},
349					String:    "string1",
350				},
351			},
352			out: [][]string{
353				{"json", "string"},
354				{`{"key":"val"}`, "string1"},
355			},
356		},
357		{
358			desc: "embedded non struct tagged types with pointer receiver MarshalCSV",
359			in: []interface{}{
360				&struct {
361					Embedded14 `csv:"json"`
362					A          Embedded14 `csv:"json2"`
363				}{
364					Embedded14: Embedded14{"key": "val"},
365					A:          Embedded14{"key1": "val1"},
366				},
367				struct {
368					*Embedded14 `csv:"json"`
369					A           *Embedded14 `csv:"json2"`
370				}{
371					Embedded14: &Embedded14{"key": "val"},
372					A:          &Embedded14{"key1": "val1"},
373				},
374			},
375			out: [][]string{
376				{"json", "json2"},
377				{`{"key":"val"}`, `{"key1":"val1"}`},
378				{`{"key":"val"}`, `{"key1":"val1"}`},
379			},
380		},
381		{
382			desc: "embedded non struct tagged types with pointer receiver MarshalText",
383			in: []interface{}{
384				&struct {
385					Embedded15 `csv:"json"`
386					A          Embedded15 `csv:"json2"`
387				}{
388					Embedded15: Embedded15{"key": "val"},
389					A:          Embedded15{"key1": "val1"},
390				},
391				struct {
392					*Embedded15 `csv:"json"`
393					A           *Embedded15 `csv:"json2"`
394				}{
395					Embedded15: &Embedded15{"key": "val"},
396					A:          &Embedded15{"key1": "val1"},
397				},
398			},
399			out: [][]string{
400				{"json", "json2"},
401				{`{"key":"val"}`, `{"key1":"val1"}`},
402				{`{"key":"val"}`, `{"key1":"val1"}`},
403			},
404		},
405		{
406			desc: "embedded pointer types",
407			in: []interface{}{
408				TypeC{
409					Embedded1: &Embedded1{
410						String: "string2",
411						Float:  1,
412					},
413					String: "string1",
414				},
415			},
416			out: [][]string{
417				{"float", "string"},
418				{`1`, "string1"},
419			},
420		},
421		{
422			desc: "embedded pointer types with nil values",
423			in: []interface{}{
424				TypeC{
425					Embedded1: nil,
426					String:    "string1",
427				},
428			},
429			out: [][]string{
430				{"float", "string"},
431				{``, "string1"},
432			},
433		},
434		{
435			desc: "embedded non struct tagged pointer types",
436			in: []interface{}{
437				TypeD{
438					Embedded3: &Embedded3{"key": "val"},
439					String:    "string1",
440				},
441			},
442			out: [][]string{
443				{"json", "string"},
444				{`{"key":"val"}`, "string1"},
445			},
446		},
447		{
448			desc: "embedded non struct tagged pointer types with nil value - textmarshaler",
449			in: []interface{}{
450				TypeM{
451					TextMarshaler: nil,
452				},
453			},
454			out: [][]string{
455				{"text"},
456				{""},
457			},
458		},
459		{
460			desc: "embedded non struct tagged pointer types with nil value - csvmarshaler",
461			in: []interface{}{
462				TypeD{
463					Embedded3: nil,
464					String:    "string1",
465				},
466			},
467			out: [][]string{
468				{"json", "string"},
469				{"", "string1"},
470			},
471		},
472		{
473			desc: "tagged fields priority",
474			in: []interface{}{
475				TagPriority{Foo: 1, Bar: 2},
476			},
477			out: [][]string{
478				{"Foo"},
479				{"2"},
480			},
481		},
482		{
483			desc: "conflicting embedded fields #1",
484			in: []interface{}{
485				Embedded5{
486					Embedded6: Embedded6{X: 60},
487					Embedded7: Embedded7{X: 70},
488					Embedded8: Embedded8{
489						Embedded9: Embedded9{
490							X: 90,
491							Y: 91,
492						},
493					},
494				},
495			},
496			out: [][]string{
497				{"Y"},
498				{"91"},
499			},
500		},
501		{
502			desc: "conflicting embedded fields #2",
503			in: []interface{}{
504				Embedded10{
505					Embedded11: Embedded11{
506						Embedded6: Embedded6{X: 60},
507					},
508					Embedded12: Embedded12{
509						Embedded6: Embedded6{X: 60},
510					},
511					Embedded13: Embedded13{
512						Embedded8: Embedded8{
513							Embedded9: Embedded9{
514								X: 90,
515								Y: 91,
516							},
517						},
518					},
519				},
520			},
521			out: [][]string{
522				{"Y"},
523				{"91"},
524			},
525		},
526		{
527			desc: "double pointer",
528			in: []interface{}{
529				TypeE{
530					String: &PString,
531					Int:    &Int,
532				},
533			},
534			out: [][]string{
535				{"string", "int"},
536				{"string", "10"},
537			},
538		},
539		{
540			desc: "nil double pointer",
541			in: []interface{}{
542				TypeE{},
543			},
544			out: [][]string{
545				{"string", "int"},
546				{"", ""},
547			},
548		},
549		{
550			desc: "unexported non-struct embedded",
551			in: []interface{}{
552				struct {
553					A int
554					embeddedMap
555				}{1, make(embeddedMap)},
556			},
557			out: [][]string{
558				{"A"},
559				{"1"},
560			},
561		},
562		{
563			desc: "cyclic reference",
564			in: []interface{}{
565				A{
566					B: B{Y: 2, A: &A{}},
567					X: 1,
568				},
569			},
570			out: [][]string{
571				{"Y", "X"},
572				{"2", "1"},
573			},
574		},
575		{
576			desc: "ptr receiver csv marshaler",
577			in: []interface{}{
578				&struct {
579					A PtrRecCSVMarshaler
580				}{},
581				struct {
582					A PtrRecCSVMarshaler
583				}{},
584				struct {
585					A *PtrRecCSVMarshaler
586				}{new(PtrRecCSVMarshaler)},
587				&struct {
588					A *PtrRecCSVMarshaler
589				}{new(PtrRecCSVMarshaler)},
590				&struct {
591					A *PtrRecCSVMarshaler
592				}{},
593			},
594			out: [][]string{
595				{"A"},
596				{"ptrreccsvmarshaler"},
597				{"0"},
598				{"ptrreccsvmarshaler"},
599				{"ptrreccsvmarshaler"},
600				{""},
601			},
602		},
603		{
604			desc: "ptr receiver text marshaler",
605			in: []interface{}{
606				&struct {
607					A PtrRecTextMarshaler
608				}{},
609				struct {
610					A PtrRecTextMarshaler
611				}{},
612				struct {
613					A *PtrRecTextMarshaler
614				}{new(PtrRecTextMarshaler)},
615				&struct {
616					A *PtrRecTextMarshaler
617				}{new(PtrRecTextMarshaler)},
618				&struct {
619					A *PtrRecTextMarshaler
620				}{},
621			},
622			out: [][]string{
623				{"A"},
624				{"ptrrectextmarshaler"},
625				{"0"},
626				{"ptrrectextmarshaler"},
627				{"ptrrectextmarshaler"},
628				{""},
629			},
630		},
631		{
632			desc: "text marshaler",
633			in: []interface{}{
634				struct {
635					A CSVMarshaler
636				}{},
637				struct {
638					A TextMarshaler
639				}{},
640				struct {
641					A struct {
642						TextMarshaler
643						CSVMarshaler
644					}
645				}{},
646			},
647			out: [][]string{
648				{"A"},
649				{"csvmarshaler"},
650				{"textmarshaler"},
651				{"csvmarshaler"},
652			},
653		},
654		{
655			desc: "primitive type alias implementing Marshaler",
656			in: []interface{}{
657				EnumType{Enum: EnumFirst},
658				EnumType{Enum: EnumSecond},
659			},
660			out: [][]string{
661				{"enum"},
662				{"first"},
663				{"second"},
664			},
665		},
666		{
667			desc: "aliased type",
668			in: []interface{}{
669				struct{ Float float64 }{3.14},
670			},
671			out: [][]string{
672				{"Float"},
673				{"3.14"},
674			},
675		},
676		{
677			desc: "embedded tagged marshalers",
678			in: []interface{}{
679				struct {
680					CSVMarshaler  `csv:"csv"`
681					TextMarshaler `csv:"text"`
682				}{},
683			},
684			out: [][]string{
685				{"csv", "text"},
686				{"csvmarshaler", "textmarshaler"},
687			},
688		},
689		{
690			desc: "embedded pointer tagged marshalers",
691			in: []interface{}{
692				struct {
693					*CSVMarshaler  `csv:"csv"`
694					*TextMarshaler `csv:"text"`
695				}{&CSVMarshaler{}, &TextMarshaler{}},
696			},
697			out: [][]string{
698				{"csv", "text"},
699				{"csvmarshaler", "textmarshaler"},
700			},
701		},
702		{
703			desc: "inline fields",
704			in: []interface{}{
705				Inline{
706					J1: TypeJ{
707						String:     "j1",
708						Int:        "1",
709						Float:      "1",
710						Embedded16: Embedded16{Bool: true, Uint8: 1},
711					},
712					J2: TypeJ{
713						String:     "j2",
714						Int:        "2",
715						Float:      "2",
716						Embedded16: Embedded16{Bool: true, Uint8: 2},
717					},
718					String:  "top-level-str",
719					String2: "STR",
720				},
721			},
722			out: [][]string{
723				{"int", "Bool", "Uint8", "float", "prefix-STR", "prefix-int", "prefix-Bool", "prefix-Uint8", "prefix-float", "top-string", "STR"},
724				{"1", "true", "1", "1", "j2", "2", "true", "2", "2", "top-level-str", "STR"},
725			},
726		},
727		{
728			desc: "inline chain",
729			in: []interface{}{
730				Inline5{
731					A: Inline2{
732						S: "1",
733						A: Inline3{
734							Inline4: Inline4{A: "11"},
735						},
736						B: Inline3{
737							Inline4: Inline4{A: "12"},
738						},
739					},
740					B: Inline2{
741						S: "2",
742						A: Inline3{
743							Inline4: Inline4{A: "21"},
744						},
745						B: Inline3{
746							Inline4: Inline4{A: "22"},
747						},
748					},
749				},
750			},
751			out: [][]string{
752				{"AS", "AAA", "S", "A"},
753				{"1", "11", "2", "22"},
754			},
755		},
756		{
757			desc: "cyclic inline - no prefix",
758			in: []interface{}{
759				Inline6{
760					A: Inline7{
761						A: &Inline6{A: Inline7{
762							A: &Inline6{},
763							X: 10,
764						}},
765						X: 1,
766					},
767				},
768			},
769			out: [][]string{
770				{"X"},
771				{"1"},
772			},
773		},
774		{
775			desc: "embedded with inline tag",
776			in: []interface{}{
777				struct {
778					Inline7 `csv:"A,inline"`
779				}{
780					Inline7: Inline7{
781						A: &Inline6{A: Inline7{
782							A: &Inline6{},
783							X: 10,
784						}},
785						X: 1,
786					},
787				},
788			},
789			out: [][]string{
790				{"AX"},
791				{"1"},
792			},
793		},
794		{
795			desc: "embedded with empty inline tag",
796			in: []interface{}{
797				struct {
798					Inline7 `csv:",inline"`
799				}{
800					Inline7: Inline7{
801						A: &Inline6{A: Inline7{
802							A: &Inline6{},
803							X: 10,
804						}},
805						X: 1,
806					},
807				},
808			},
809			out: [][]string{
810				{"X"},
811				{"1"},
812			},
813		},
814		{
815			desc: "embedded with ptr inline tag",
816			in: []interface{}{
817				struct {
818					*Inline7 `csv:"A,inline"`
819				}{
820					Inline7: &Inline7{
821						A: &Inline6{A: Inline7{
822							A: &Inline6{},
823							X: 10,
824						}},
825						X: 1,
826					},
827				},
828			},
829			out: [][]string{
830				{"AX"},
831				{"1"},
832			},
833		},
834		{
835			desc: "inline visibility rules - top field first",
836			in: []interface{}{
837				struct {
838					AA string
839					F  Inline4 `csv:"A,inline"`
840				}{
841					AA: "1",
842					F:  Inline4{A: "10"},
843				},
844			},
845			out: [][]string{
846				{"AA"},
847				{"1"},
848			},
849		},
850		{
851			desc: "inline visibility rules - top field last",
852			in: []interface{}{
853				Inline8{
854					F:  &Inline4{A: "10"},
855					AA: 1,
856				},
857			},
858			out: [][]string{
859				{"AA"},
860				{"1"},
861			},
862		},
863		{
864			desc: "ignore inline tag on non struct",
865			in: []interface{}{
866				struct {
867					X int `csv:",inline"`
868					Y int `csv:"y,inline"`
869				}{
870					X: 1,
871					Y: 2,
872				},
873			},
874			out: [][]string{
875				{"X", "y"},
876				{"1", "2"},
877			},
878		},
879		{
880			desc: "registered func - non ptr elem",
881			in: []interface{}{
882				struct {
883					Int    int
884					Pint   *int
885					Iface  interface{}
886					Piface *interface{}
887				}{
888					Pint:   pint(0),
889					Iface:  34,
890					Piface: pinterface(34),
891				},
892			},
893			regFunc: []interface{}{
894				func(int) ([]byte, error) { return []byte("int"), nil },
895			},
896			out: [][]string{
897				{"Int", "Pint", "Iface", "Piface"},
898				{"int", "int", "int", "int"},
899			},
900		},
901		{
902			desc: "registered func - ptr elem",
903			in: []interface{}{
904				&struct {
905					Int    int
906					Pint   *int
907					Iface  interface{}
908					Piface *interface{}
909				}{
910					Pint:   pint(0),
911					Iface:  34,
912					Piface: pinterface(34),
913				},
914			},
915			regFunc: []interface{}{
916				func(int) ([]byte, error) { return []byte("int"), nil },
917			},
918			out: [][]string{
919				{"Int", "Pint", "Iface", "Piface"},
920				{"int", "int", "int", "int"},
921			},
922		},
923		{
924			desc: "registered func - ptr type - non ptr elem",
925			in: []interface{}{
926				struct {
927					Int    int
928					Pint   *int
929					Iface  interface{}
930					Piface *interface{}
931				}{
932					Pint:   pint(0),
933					Iface:  34,
934					Piface: pinterface(pint(34)),
935				},
936			},
937			regFunc: []interface{}{
938				func(*int) ([]byte, error) { return []byte("int"), nil },
939			},
940			out: [][]string{
941				{"Int", "Pint", "Iface", "Piface"},
942				{"0", "int", "34", "int"},
943			},
944		},
945		{
946			desc: "registered func - ptr type - ptr elem",
947			in: []interface{}{
948				&struct {
949					Int    int
950					Pint   *int
951					Iface  interface{}
952					Piface *interface{}
953				}{
954					Pint:   pint(0),
955					Iface:  34,
956					Piface: pinterface(pint(34)),
957				},
958			},
959			regFunc: []interface{}{
960				func(*int) ([]byte, error) { return []byte("int"), nil },
961			},
962			out: [][]string{
963				{"Int", "Pint", "Iface", "Piface"},
964				{"int", "int", "34", "int"},
965			},
966		},
967		{
968			desc: "registered func - mixed types - non ptr elem",
969			in: []interface{}{
970				struct {
971					Int    int
972					Pint   *int
973					Iface  interface{}
974					Piface *interface{}
975				}{
976					Pint:   pint(0),
977					Iface:  34,
978					Piface: pinterface(pint(34)),
979				},
980			},
981			regFunc: []interface{}{
982				func(int) ([]byte, error) { return []byte("int"), nil },
983				func(*int) ([]byte, error) { return []byte("*int"), nil },
984			},
985			out: [][]string{
986				{"Int", "Pint", "Iface", "Piface"},
987				{"int", "*int", "int", "*int"},
988			},
989		},
990		{
991			desc: "registered func - mixed types - ptr elem",
992			in: []interface{}{
993				&struct {
994					Int    int
995					Pint   *int
996					Iface  interface{}
997					Piface *interface{}
998				}{
999					Pint:   pint(0),
1000					Iface:  34,
1001					Piface: pinterface(pint(34)),
1002				},
1003			},
1004			regFunc: []interface{}{
1005				func(int) ([]byte, error) { return []byte("int"), nil },
1006				func(*int) ([]byte, error) { return []byte("*int"), nil },
1007			},
1008			out: [][]string{
1009				{"Int", "Pint", "Iface", "Piface"},
1010				{"int", "*int", "int", "*int"},
1011			},
1012		},
1013		{
1014			desc: "registered func - interfaces",
1015			in: []interface{}{
1016				&struct {
1017					CSVMarshaler        Marshaler
1018					Marshaler           CSVMarshaler
1019					PMarshaler          *CSVMarshaler
1020					CSVTextMarshaler    CSVTextMarshaler
1021					PCSVTextMarshaler   *CSVTextMarshaler
1022					PtrRecCSVMarshaler  PtrRecCSVMarshaler
1023					PtrRecTextMarshaler PtrRecTextMarshaler
1024				}{
1025					PMarshaler:        &CSVMarshaler{},
1026					PCSVTextMarshaler: &CSVTextMarshaler{},
1027				},
1028			},
1029			regFunc: []interface{}{
1030				func(Marshaler) ([]byte, error) { return []byte("registered.marshaler"), nil },
1031				func(encoding.TextMarshaler) ([]byte, error) { return []byte("registered.textmarshaler"), nil },
1032			},
1033			out: [][]string{
1034				{"CSVMarshaler", "Marshaler", "PMarshaler", "CSVTextMarshaler", "PCSVTextMarshaler", "PtrRecCSVMarshaler", "PtrRecTextMarshaler"},
1035				{"registered.marshaler", "registered.marshaler", "registered.marshaler", "registered.marshaler", "registered.marshaler", "registered.marshaler", "registered.textmarshaler"},
1036			},
1037		},
1038		{
1039			desc: "registered func - interface order",
1040			in: []interface{}{
1041				&struct {
1042					CSVTextMarshaler  CSVTextMarshaler
1043					PCSVTextMarshaler *CSVTextMarshaler
1044				}{
1045					PCSVTextMarshaler: &CSVTextMarshaler{},
1046				},
1047			},
1048			regFunc: []interface{}{
1049				func(encoding.TextMarshaler) ([]byte, error) { return []byte("registered.textmarshaler"), nil },
1050				func(Marshaler) ([]byte, error) { return []byte("registered.marshaler"), nil },
1051			},
1052			out: [][]string{
1053				{"CSVTextMarshaler", "PCSVTextMarshaler"},
1054				{"registered.textmarshaler", "registered.textmarshaler"},
1055			},
1056		},
1057		{
1058			desc: "registered func - method",
1059			in: []interface{}{
1060				&struct {
1061					PtrRecCSVMarshaler PtrRecCSVMarshaler
1062				}{},
1063				struct {
1064					PtrRecCSVMarshaler PtrRecCSVMarshaler
1065				}{},
1066			},
1067			regFunc: []interface{}{
1068				(*PtrRecCSVMarshaler).CSV,
1069			},
1070			out: [][]string{
1071				{"PtrRecCSVMarshaler"},
1072				{"ptrreccsvmarshaler.CSV"},
1073				{"0"},
1074			},
1075		},
1076		{
1077			desc: "registered func - fallback error",
1078			in: []interface{}{
1079				struct {
1080					Embedded14
1081				}{},
1082			},
1083			regFunc: []interface{}{
1084				(*Embedded14).MarshalCSV,
1085			},
1086			err: &UnsupportedTypeError{
1087				Type: reflect.TypeOf(Embedded14{}),
1088			},
1089		},
1090		{
1091			desc: "registered interface func - returning error",
1092			in: []interface{}{
1093				&struct {
1094					Embedded14 Embedded14
1095				}{},
1096			},
1097			regFunc: []interface{}{
1098				func(Marshaler) ([]byte, error) { return nil, Error },
1099			},
1100			err: Error,
1101		},
1102		{
1103			desc: "registered func - returning error",
1104			in: []interface{}{
1105				&struct {
1106					A InvalidType
1107				}{},
1108			},
1109			regFunc: []interface{}{
1110				func(*InvalidType) ([]byte, error) { return nil, Error },
1111			},
1112			err: Error,
1113		},
1114		{
1115			desc: "registered func - fallback error on interface",
1116			in: []interface{}{
1117				struct {
1118					Embedded14
1119				}{},
1120			},
1121			regFunc: []interface{}{
1122				func(m Marshaler) ([]byte, error) { return nil, nil },
1123			},
1124			err: &UnsupportedTypeError{
1125				Type: reflect.TypeOf(Embedded14{}),
1126			},
1127		},
1128		{
1129			desc: "marshaler fallback error",
1130			in: []interface{}{
1131				struct {
1132					Embedded14
1133				}{},
1134			},
1135			err: &UnsupportedTypeError{
1136				Type: reflect.TypeOf(Embedded14{}),
1137			},
1138		},
1139		{
1140			desc: "encode different types",
1141			// This doesnt mean the output csv is valid. Generally this is an invalid
1142			// use. However, we need to make sure that the encoder is doing what it is
1143			// asked to... correctly.
1144			in: []interface{}{
1145				struct {
1146					A int
1147				}{},
1148				struct {
1149					A int
1150					B string
1151				}{},
1152				struct {
1153					A int
1154				}{},
1155				struct{}{},
1156			},
1157			out: [][]string{
1158				{"A"},
1159				{"0"},
1160				{"0", ""},
1161				{"0"},
1162				{},
1163			},
1164		},
1165		{
1166			desc: "encode interface values",
1167			in: []interface{}{
1168				struct {
1169					V interface{}
1170				}{1},
1171				struct {
1172					V interface{}
1173				}{pint(10)},
1174				struct {
1175					V interface{}
1176				}{ppint(100)},
1177				struct {
1178					V interface{}
1179				}{pppint(1000)},
1180				struct {
1181					V *interface{}
1182				}{pinterface(ppint(10000))},
1183				struct {
1184					V *interface{}
1185				}{func() *interface{} {
1186					var v interface{} = pppint(100000)
1187					var vv interface{} = v
1188					return &vv
1189				}()},
1190				struct {
1191					V interface{}
1192				}{func() interface{} {
1193					var v interface{} = &CSVMarshaler{}
1194					var vv interface{} = v
1195					return &vv
1196				}()},
1197				struct {
1198					V interface{}
1199				}{func() interface{} {
1200					var v interface{} = CSVMarshaler{}
1201					var vv interface{} = v
1202					return &vv
1203				}()},
1204				struct {
1205					V interface{}
1206				}{func() interface{} {
1207					var v interface{} = &CSVMarshaler{}
1208					var vv interface{} = v
1209					return vv
1210				}()},
1211				struct {
1212					V interface{}
1213				}{
1214					V: func() interface{} {
1215						return PtrRecCSVMarshaler(5)
1216					}(),
1217				},
1218				struct {
1219					V interface{}
1220				}{
1221					V: func() interface{} {
1222						m := PtrRecCSVMarshaler(5)
1223						return &m
1224					}(),
1225				},
1226				struct {
1227					V interface{}
1228				}{func() interface{} {
1229					var v interface{}
1230					var vv interface{} = v
1231					return &vv
1232				}()},
1233			},
1234			out: [][]string{
1235				{"V"},
1236				{"1"},
1237				{"10"},
1238				{"100"},
1239				{"1000"},
1240				{"10000"},
1241				{"100000"},
1242				{"csvmarshaler"},
1243				{"csvmarshaler"},
1244				{"csvmarshaler"},
1245				{"5"},
1246				{"ptrreccsvmarshaler"},
1247				{""},
1248			},
1249		},
1250		{
1251			desc: "encode NaN",
1252			in: []interface{}{
1253				struct {
1254					Float float64
1255				}{math.NaN()},
1256			},
1257			out: [][]string{
1258				{"Float"},
1259				{"NaN"},
1260			},
1261		},
1262		{
1263			desc: "encode NaN with aliased type",
1264			in: []interface{}{
1265				struct {
1266					Float Float
1267				}{Float(math.NaN())},
1268			},
1269			out: [][]string{
1270				{"Float"},
1271				{"NaN"},
1272			},
1273		},
1274		{
1275			desc: "empty struct",
1276			in: []interface{}{
1277				struct{}{},
1278			},
1279			out: [][]string{{}, {}},
1280		},
1281		{
1282			desc: "value wrapped in interfaces and pointers",
1283			in: []interface{}{
1284				func() (v interface{}) { v = &struct{ A int }{5}; return v }(),
1285			},
1286			out: [][]string{{"A"}, {"5"}},
1287		},
1288		{
1289			desc: "csv marshaler error",
1290			in: []interface{}{
1291				struct {
1292					A CSVMarshaler
1293				}{
1294					A: CSVMarshaler{Err: Error},
1295				},
1296			},
1297			err: &MarshalerError{Type: reflect.TypeOf(CSVMarshaler{}), MarshalerType: "MarshalCSV", Err: Error},
1298		},
1299		{
1300			desc: "csv marshaler error as registered error",
1301			in: []interface{}{
1302				struct {
1303					A CSVMarshaler
1304				}{
1305					A: CSVMarshaler{Err: Error},
1306				},
1307			},
1308			regFunc: []interface{}{
1309				CSVMarshaler.MarshalCSV,
1310			},
1311			err: Error,
1312		},
1313		{
1314			desc: "text marshaler error",
1315			in: []interface{}{
1316				struct {
1317					A TextMarshaler
1318				}{
1319					A: TextMarshaler{Err: Error},
1320				},
1321			},
1322			err: &MarshalerError{Type: reflect.TypeOf(TextMarshaler{}), MarshalerType: "MarshalText", Err: Error},
1323		},
1324		{
1325			desc: "text marshaler fallback error - ptr reciever",
1326			in: []interface{}{
1327				struct {
1328					A Embedded15
1329				}{},
1330			},
1331			err: &UnsupportedTypeError{Type: reflect.TypeOf(Embedded15{})},
1332		},
1333		{
1334			desc: "text marshaler error as registered func",
1335			in: []interface{}{
1336				struct {
1337					A TextMarshaler
1338				}{
1339					A: TextMarshaler{Err: Error},
1340				},
1341			},
1342			regFunc: []interface{}{
1343				TextMarshaler.MarshalText,
1344			},
1345			err: Error,
1346		},
1347		{
1348			desc: "unsupported type",
1349			in: []interface{}{
1350				InvalidType{},
1351			},
1352			err: &UnsupportedTypeError{
1353				Type: reflect.TypeOf(struct{}{}),
1354			},
1355		},
1356		{
1357			desc: "unsupported double pointer type",
1358			in: []interface{}{
1359				struct {
1360					A **struct{}
1361				}{},
1362			},
1363			err: &UnsupportedTypeError{
1364				Type: reflect.TypeOf(struct{}{}),
1365			},
1366		},
1367		{
1368			desc: "unsupported interface type",
1369			in: []interface{}{
1370				TypeF{V: TypeA{}},
1371			},
1372			err: &UnsupportedTypeError{
1373				Type: reflect.TypeOf(TypeA{}),
1374			},
1375		},
1376		{
1377			desc: "encode not a struct",
1378			in:   []interface{}{int(1)},
1379			err: &InvalidEncodeError{
1380				Type: reflect.TypeOf(int(1)),
1381			},
1382		},
1383		{
1384			desc: "encode nil interface",
1385			in:   []interface{}{nilIface},
1386			err: &InvalidEncodeError{
1387				Type: reflect.TypeOf(nilIface),
1388			},
1389		},
1390		{
1391			desc: "encode nil ptr",
1392			in:   []interface{}{nilPtr},
1393			err:  &InvalidEncodeError{},
1394		},
1395		{
1396			desc: "encode nil interface pointer",
1397			in:   []interface{}{nilIfacePtr},
1398			err:  &InvalidEncodeError{},
1399		},
1400	}
1401
1402	for _, f := range fixtures {
1403		t.Run(f.desc, func(t *testing.T) {
1404			var buf bytes.Buffer
1405			w := csv.NewWriter(&buf)
1406			enc := NewEncoder(w)
1407
1408			for _, f := range f.regFunc {
1409				enc.Register(f)
1410			}
1411
1412			for _, v := range f.in {
1413				err := enc.Encode(v)
1414				if f.err != nil {
1415					if !reflect.DeepEqual(f.err, err) {
1416						t.Errorf("want err=%v; got %v", f.err, err)
1417					}
1418					return
1419				} else if err != nil {
1420					t.Errorf("want err=nil; got %v", err)
1421				}
1422			}
1423			w.Flush()
1424			if err := w.Error(); err != nil {
1425				t.Errorf("want err=nil; got %v", err)
1426			}
1427
1428			var out bytes.Buffer
1429			if err := csv.NewWriter(&out).WriteAll(f.out); err != nil {
1430				t.Errorf("want err=nil; got %v", err)
1431			}
1432
1433			if buf.String() != out.String() {
1434				t.Errorf("want=%s; got %s", out.String(), buf.String())
1435			}
1436		})
1437	}
1438
1439	t.Run("test decoder tags", func(t *testing.T) {
1440		type Test struct {
1441			A int     `custom:"1"`
1442			B string  `custom:"2"`
1443			C float64 `custom:"-"`
1444		}
1445
1446		test := &Test{
1447			A: 1,
1448			B: "b",
1449			C: 2.5,
1450		}
1451
1452		var bufs [4]bytes.Buffer
1453		for i := 0; i < 4; i += 2 {
1454			encode(t, &bufs[i], test, "")
1455			encode(t, &bufs[i+1], test, "custom")
1456		}
1457
1458		if b1, b2 := bufs[0].String(), bufs[2].String(); b1 != b2 {
1459			t.Errorf("buffers are not equal: %s vs %s", b1, b2)
1460		}
1461		if b1, b2 := bufs[1].String(), bufs[3].String(); b1 != b2 {
1462			t.Errorf("buffers are not equal: %s vs %s", b1, b2)
1463		}
1464
1465		expected1 := [][]string{
1466			{"A", "B", "C"},
1467			{"1", "b", "2.5"},
1468		}
1469		expected2 := [][]string{
1470			{"1", "2"},
1471			{"1", "b"},
1472		}
1473
1474		if b1, b2 := bufs[0].String(), encodeCSV(t, expected1); b1 != b2 {
1475			t.Errorf("want buf=%s; got %s", b2, b1)
1476		}
1477		if b1, b2 := bufs[1].String(), encodeCSV(t, expected2); b1 != b2 {
1478			t.Errorf("want buf=%s; got %s", b2, b1)
1479		}
1480	})
1481
1482	t.Run("error messages", func(t *testing.T) {
1483		fixtures := []struct {
1484			desc     string
1485			expected string
1486			v        interface{}
1487		}{
1488			{
1489				desc:     "invalid encode error message",
1490				expected: "csvutil: Encode(int64)",
1491				v:        int64(1),
1492			},
1493			{
1494				desc:     "invalid encode error message with nil interface",
1495				expected: "csvutil: Encode(nil)",
1496				v:        nilIface,
1497			},
1498			{
1499				desc:     "invalid encode error message with nil value",
1500				expected: "csvutil: Encode(nil)",
1501				v:        nilPtr,
1502			},
1503			{
1504				desc:     "unsupported type error message",
1505				expected: "csvutil: unsupported type: struct {}",
1506				v:        struct{ InvalidType }{},
1507			},
1508			{
1509				desc:     "marshaler error message",
1510				expected: "csvutil: error calling MarshalText for type csvutil.TextMarshaler: " + Error.Error(),
1511				v:        struct{ M TextMarshaler }{TextMarshaler{Error}},
1512			},
1513		}
1514
1515		for _, f := range fixtures {
1516			t.Run(f.desc, func(t *testing.T) {
1517				err := NewEncoder(csv.NewWriter(bytes.NewBuffer(nil))).Encode(f.v)
1518				if err == nil {
1519					t.Fatal("want err not to be nil")
1520				}
1521				if err.Error() != f.expected {
1522					t.Errorf("want=%s; got %s", f.expected, err.Error())
1523				}
1524			})
1525		}
1526	})
1527
1528	t.Run("EncodeHeader", func(t *testing.T) {
1529		t.Run("no double header with encode", func(t *testing.T) {
1530			var buf bytes.Buffer
1531			w := csv.NewWriter(&buf)
1532			enc := NewEncoder(w)
1533			if err := enc.EncodeHeader(TypeI{}); err != nil {
1534				t.Errorf("want err=nil; got %v", err)
1535			}
1536			if err := enc.Encode(TypeI{}); err != nil {
1537				t.Errorf("want err=nil; got %v", err)
1538			}
1539			w.Flush()
1540
1541			expected := encodeCSV(t, [][]string{
1542				{"String", "int"},
1543				{"", ""},
1544			})
1545
1546			if buf.String() != expected {
1547				t.Errorf("want out=%s; got %s", expected, buf.String())
1548			}
1549		})
1550
1551		t.Run("encode writes header if EncodeHeader fails", func(t *testing.T) {
1552			var buf bytes.Buffer
1553			w := csv.NewWriter(&buf)
1554			enc := NewEncoder(w)
1555
1556			if err := enc.EncodeHeader(InvalidType{}); err == nil {
1557				t.Errorf("expected not nil error")
1558			}
1559
1560			if err := enc.Encode(TypeI{}); err != nil {
1561				t.Errorf("want err=nil; got %v", err)
1562			}
1563
1564			w.Flush()
1565
1566			expected := encodeCSV(t, [][]string{
1567				{"String", "int"},
1568				{"", ""},
1569			})
1570
1571			if buf.String() != expected {
1572				t.Errorf("want out=%s; got %s", expected, buf.String())
1573			}
1574		})
1575
1576		fixtures := []struct {
1577			desc string
1578			in   interface{}
1579			tag  string
1580			out  [][]string
1581			err  error
1582		}{
1583			{
1584				desc: "conflicting fields",
1585				in:   &Embedded10{},
1586				out: [][]string{
1587					{"Y"},
1588				},
1589			},
1590			{
1591				desc: "custom tag",
1592				in:   TypeJ{},
1593				tag:  "json",
1594				out: [][]string{
1595					{"string", "bool", "Uint", "Float"},
1596				},
1597			},
1598			{
1599				desc: "nil interface ptr value",
1600				in:   nilIfacePtr,
1601				out: [][]string{
1602					{
1603						"int",
1604						"pint",
1605						"int8",
1606						"pint8",
1607						"int16",
1608						"pint16",
1609						"int32",
1610						"pint32",
1611						"int64",
1612						"pint64",
1613						"uint",
1614						"puint",
1615						"uint8",
1616						"puint8",
1617						"uint16",
1618						"puint16",
1619						"uint32",
1620						"puint32",
1621						"uint64",
1622						"puint64",
1623						"float32",
1624						"pfloat32",
1625						"float64",
1626						"pfloat64",
1627						"string",
1628						"pstring",
1629						"bool",
1630						"pbool",
1631						"interface",
1632						"pinterface",
1633						"binary",
1634						"pbinary",
1635					},
1636				},
1637			},
1638			{
1639				desc: "ptr to nil interface ptr value",
1640				in:   &nilIfacePtr,
1641				out: [][]string{
1642					{
1643						"int",
1644						"pint",
1645						"int8",
1646						"pint8",
1647						"int16",
1648						"pint16",
1649						"int32",
1650						"pint32",
1651						"int64",
1652						"pint64",
1653						"uint",
1654						"puint",
1655						"uint8",
1656						"puint8",
1657						"uint16",
1658						"puint16",
1659						"uint32",
1660						"puint32",
1661						"uint64",
1662						"puint64",
1663						"float32",
1664						"pfloat32",
1665						"float64",
1666						"pfloat64",
1667						"string",
1668						"pstring",
1669						"bool",
1670						"pbool",
1671						"interface",
1672						"pinterface",
1673						"binary",
1674						"pbinary",
1675					},
1676				},
1677			},
1678			{
1679				desc: "nil ptr value",
1680				in:   nilPtr,
1681				out: [][]string{
1682					{
1683						"int",
1684						"pint",
1685						"int8",
1686						"pint8",
1687						"int16",
1688						"pint16",
1689						"int32",
1690						"pint32",
1691						"int64",
1692						"pint64",
1693						"uint",
1694						"puint",
1695						"uint8",
1696						"puint8",
1697						"uint16",
1698						"puint16",
1699						"uint32",
1700						"puint32",
1701						"uint64",
1702						"puint64",
1703						"float32",
1704						"pfloat32",
1705						"float64",
1706						"pfloat64",
1707						"string",
1708						"pstring",
1709						"bool",
1710						"pbool",
1711						"interface",
1712						"pinterface",
1713						"binary",
1714						"pbinary",
1715					},
1716				},
1717			},
1718			{
1719				desc: "ptr to nil ptr value",
1720				in:   &nilPtr,
1721				out: [][]string{
1722					{
1723						"int",
1724						"pint",
1725						"int8",
1726						"pint8",
1727						"int16",
1728						"pint16",
1729						"int32",
1730						"pint32",
1731						"int64",
1732						"pint64",
1733						"uint",
1734						"puint",
1735						"uint8",
1736						"puint8",
1737						"uint16",
1738						"puint16",
1739						"uint32",
1740						"puint32",
1741						"uint64",
1742						"puint64",
1743						"float32",
1744						"pfloat32",
1745						"float64",
1746						"pfloat64",
1747						"string",
1748						"pstring",
1749						"bool",
1750						"pbool",
1751						"interface",
1752						"pinterface",
1753						"binary",
1754						"pbinary",
1755					},
1756				},
1757			},
1758			{
1759				desc: "ptr to nil interface",
1760				in:   &nilIface,
1761				err:  &UnsupportedTypeError{Type: reflect.ValueOf(&nilIface).Type().Elem()},
1762			},
1763			{
1764				desc: "nil value",
1765				err:  &UnsupportedTypeError{},
1766			},
1767			{
1768				desc: "ptr - not a struct",
1769				in:   &[]int{},
1770				err:  &UnsupportedTypeError{Type: reflect.TypeOf([]int{})},
1771			},
1772			{
1773				desc: "not a struct",
1774				in:   int(1),
1775				err:  &UnsupportedTypeError{Type: reflect.TypeOf(int(0))},
1776			},
1777		}
1778
1779		for _, f := range fixtures {
1780			t.Run(f.desc, func(t *testing.T) {
1781				var buf bytes.Buffer
1782				w := csv.NewWriter(&buf)
1783
1784				enc := NewEncoder(w)
1785				enc.Tag = f.tag
1786
1787				err := enc.EncodeHeader(f.in)
1788				w.Flush()
1789
1790				if !reflect.DeepEqual(err, f.err) {
1791					t.Errorf("want err=%v; got %v", f.err, err)
1792				}
1793
1794				if f.err != nil {
1795					return
1796				}
1797
1798				if expected := encodeCSV(t, f.out); buf.String() != expected {
1799					t.Errorf("want out=%s; got %s", expected, buf.String())
1800				}
1801			})
1802		}
1803	})
1804
1805	t.Run("AutoHeader false", func(t *testing.T) {
1806		var buf bytes.Buffer
1807		w := csv.NewWriter(&buf)
1808		enc := NewEncoder(w)
1809		enc.AutoHeader = false
1810
1811		if err := enc.Encode(TypeG{
1812			String: "s",
1813			Int:    10,
1814		}); err != nil {
1815			t.Fatalf("want err=nil; got %v", err)
1816		}
1817		w.Flush()
1818
1819		expected := encodeCSV(t, [][]string{{"s", "10"}})
1820		if expected != buf.String() {
1821			t.Errorf("want %s; got %s", expected, buf.String())
1822		}
1823	})
1824
1825	t.Run("fail on type encoding without header", func(t *testing.T) {
1826		var buf bytes.Buffer
1827		w := csv.NewWriter(&buf)
1828		enc := NewEncoder(w)
1829		enc.AutoHeader = false
1830
1831		err := enc.Encode(struct {
1832			Invalid InvalidType
1833		}{})
1834
1835		expected := &UnsupportedTypeError{Type: reflect.TypeOf(InvalidType{})}
1836		if !reflect.DeepEqual(err, expected) {
1837			t.Errorf("want %v; got %v", expected, err)
1838		}
1839	})
1840
1841	t.Run("fail while writing header", func(t *testing.T) {
1842		Error := errors.New("error")
1843		enc := NewEncoder(failingWriter{Err: Error})
1844		if err := enc.EncodeHeader(TypeA{}); err != Error {
1845			t.Errorf("want %v; got %v", Error, err)
1846		}
1847	})
1848
1849	t.Run("slice and array", func(t *testing.T) {
1850		fixtures := []struct {
1851			desc string
1852			in   interface{}
1853			out  [][]string
1854			err  error
1855		}{
1856			{
1857				desc: "slice",
1858				in: []TypeI{
1859					{"1", 1},
1860					{"2", 2},
1861				},
1862				out: [][]string{
1863					{"String", "int"},
1864					{"1", "1"},
1865					{"2", "2"},
1866				},
1867			},
1868			{
1869				desc: "ptr slice",
1870				in: &[]TypeI{
1871					{"1", 1},
1872					{"2", 2},
1873				},
1874				out: [][]string{
1875					{"String", "int"},
1876					{"1", "1"},
1877					{"2", "2"},
1878				},
1879			},
1880			{
1881				desc: "ptr slice with ptr elements",
1882				in: &[]*TypeI{
1883					{"1", 1},
1884					{"2", 2},
1885				},
1886				out: [][]string{
1887					{"String", "int"},
1888					{"1", "1"},
1889					{"2", "2"},
1890				},
1891			},
1892			{
1893				desc: "array",
1894				in: [2]TypeI{
1895					{"1", 1},
1896					{"2", 2},
1897				},
1898				out: [][]string{
1899					{"String", "int"},
1900					{"1", "1"},
1901					{"2", "2"},
1902				},
1903			},
1904			{
1905				desc: "ptr array",
1906				in: &[2]TypeI{
1907					{"1", 1},
1908					{"2", 2},
1909				},
1910				out: [][]string{
1911					{"String", "int"},
1912					{"1", "1"},
1913					{"2", "2"},
1914				},
1915			},
1916			{
1917				desc: "ptr array with ptr elements",
1918				in: &[2]*TypeI{
1919					{"1", 1},
1920					{"2", 2},
1921				},
1922				out: [][]string{
1923					{"String", "int"},
1924					{"1", "1"},
1925					{"2", "2"},
1926				},
1927			},
1928			{
1929				desc: "array with default val",
1930				in: [2]TypeI{
1931					{"1", 1},
1932				},
1933				out: [][]string{
1934					{"String", "int"},
1935					{"1", "1"},
1936					{"", ""},
1937				},
1938			},
1939			{
1940				desc: "no auto header on empty slice",
1941				in:   []TypeI{},
1942				out:  [][]string{},
1943			},
1944			{
1945				desc: "no auto header on empty array",
1946				in:   [0]TypeI{},
1947				out:  [][]string{},
1948			},
1949			{
1950				desc: "disallow double slice",
1951				in: [][]TypeI{
1952					{
1953						{"1", 1},
1954					},
1955				},
1956				err: &InvalidEncodeError{Type: reflect.TypeOf([][]TypeI{})},
1957			},
1958			{
1959				desc: "disallow double ptr slice",
1960				in: &[][]TypeI{
1961					{
1962						{"1", 1},
1963					},
1964				},
1965				err: &InvalidEncodeError{Type: reflect.TypeOf(&[][]TypeI{})},
1966			},
1967			{
1968				desc: "disallow double ptr slice with ptr slice",
1969				in: &[]*[]TypeI{
1970					{
1971						{"1", 1},
1972					},
1973				},
1974				err: &InvalidEncodeError{Type: reflect.TypeOf(&[]*[]TypeI{})},
1975			},
1976			{
1977				desc: "disallow double array",
1978				in: [2][2]TypeI{
1979					{
1980						{"1", 1},
1981					},
1982				},
1983				err: &InvalidEncodeError{Type: reflect.TypeOf([2][2]TypeI{})},
1984			},
1985			{
1986				desc: "disallow double ptr array",
1987				in: &[2][2]TypeI{
1988					{
1989						{"1", 1},
1990					},
1991				},
1992				err: &InvalidEncodeError{Type: reflect.TypeOf(&[2][2]TypeI{})},
1993			},
1994			{
1995				desc: "disallow interface slice",
1996				in: []interface{}{
1997					TypeI{"1", 1},
1998				},
1999				err: &InvalidEncodeError{Type: reflect.TypeOf([]interface{}{})},
2000			},
2001			{
2002				desc: "disallow interface array",
2003				in: [1]interface{}{
2004					TypeI{"1", 1},
2005				},
2006				err: &InvalidEncodeError{Type: reflect.TypeOf([1]interface{}{})},
2007			},
2008		}
2009
2010		for _, f := range fixtures {
2011			t.Run(f.desc, func(t *testing.T) {
2012				var buf bytes.Buffer
2013				w := csv.NewWriter(&buf)
2014				err := NewEncoder(w).Encode(f.in)
2015
2016				if f.err != nil {
2017					if !reflect.DeepEqual(f.err, err) {
2018						t.Errorf("want err=%v; got %v", f.err, err)
2019					}
2020					return
2021				}
2022
2023				if err != nil {
2024					t.Fatalf("want err=nil; got %v", err)
2025				}
2026
2027				w.Flush()
2028				if err := w.Error(); err != nil {
2029					t.Errorf("want err=nil; got %v", err)
2030				}
2031
2032				var out bytes.Buffer
2033				if err := csv.NewWriter(&out).WriteAll(f.out); err != nil {
2034					t.Errorf("want err=nil; got %v", err)
2035				}
2036
2037				if buf.String() != out.String() {
2038					t.Errorf("want=%s; got %s", out.String(), buf.String())
2039				}
2040			})
2041		}
2042	})
2043
2044	t.Run("register panics", func(t *testing.T) {
2045		var buf bytes.Buffer
2046		r := csv.NewWriter(&buf)
2047		enc := NewEncoder(r)
2048
2049		fixtures := []struct {
2050			desc string
2051			arg  interface{}
2052		}{
2053			{
2054				desc: "not a func",
2055				arg:  1,
2056			},
2057			{
2058				desc: "nil",
2059				arg:  nil,
2060			},
2061			{
2062				desc: "T == empty interface",
2063				arg:  func(interface{}) ([]byte, error) { return nil, nil },
2064			},
2065			{
2066				desc: "first out not bytes",
2067				arg:  func(int) (int, error) { return 0, nil },
2068			},
2069			{
2070				desc: "second out not error",
2071				arg:  func(int) (int, int) { return 0, 0 },
2072			},
2073			{
2074				desc: "func with one out value",
2075				arg:  func(int) error { return nil },
2076			},
2077			{
2078				desc: "func with no returns",
2079				arg:  func(int) {},
2080			},
2081		}
2082
2083		for _, f := range fixtures {
2084			t.Run(f.desc, func(t *testing.T) {
2085				var e interface{}
2086				func() {
2087					defer func() {
2088						e = recover()
2089					}()
2090					enc.Register(f.arg)
2091				}()
2092
2093				if e == nil {
2094					t.Error("Register was supposed to panic but it didnt")
2095				}
2096				t.Log(e)
2097			})
2098		}
2099
2100		t.Run("already registered", func(t *testing.T) {
2101			f := func(int) ([]byte, error) { return nil, nil }
2102			enc.Register(f)
2103
2104			var e interface{}
2105			func() {
2106				defer func() {
2107					e = recover()
2108				}()
2109				enc.Register(f)
2110			}()
2111
2112			if e == nil {
2113				t.Error("Register was supposed to panic but it didnt")
2114			}
2115			t.Log(e)
2116		})
2117	})
2118}
2119
2120func encode(t *testing.T, buf *bytes.Buffer, v interface{}, tag string) {
2121	w := csv.NewWriter(buf)
2122	enc := NewEncoder(w)
2123	enc.Tag = tag
2124	if err := enc.Encode(v); err != nil {
2125		t.Fatalf("want err=nil; got %v", err)
2126	}
2127	w.Flush()
2128	if err := w.Error(); err != nil {
2129		t.Fatalf("want err=nil; got %v", err)
2130	}
2131}
2132
2133func encodeCSV(t *testing.T, recs [][]string) string {
2134	var buf bytes.Buffer
2135	if err := csv.NewWriter(&buf).WriteAll(recs); err != nil {
2136		t.Fatalf("want err=nil; got %v", err)
2137	}
2138	return buf.String()
2139}
2140
2141type failingWriter struct {
2142	Err error
2143}
2144
2145func (w failingWriter) Write([]string) error {
2146	return w.Err
2147}
2148