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