diff options
Diffstat (limited to 'vendor/github.com/gocarina')
| -rw-r--r-- | vendor/github.com/gocarina/gocsv/.travis.yml | 1 | ||||
| -rw-r--r-- | vendor/github.com/gocarina/gocsv/README.md | 151 | ||||
| -rw-r--r-- | vendor/github.com/gocarina/gocsv/csv.go | 31 | ||||
| -rw-r--r-- | vendor/github.com/gocarina/gocsv/decode_test.go | 469 | ||||
| -rw-r--r-- | vendor/github.com/gocarina/gocsv/encode.go | 5 | ||||
| -rw-r--r-- | vendor/github.com/gocarina/gocsv/encode_test.go | 265 | ||||
| -rw-r--r-- | vendor/github.com/gocarina/gocsv/sample_structs_test.go | 43 | ||||
| -rw-r--r-- | vendor/github.com/gocarina/gocsv/types.go | 1 |
8 files changed, 961 insertions, 5 deletions
diff --git a/vendor/github.com/gocarina/gocsv/.travis.yml b/vendor/github.com/gocarina/gocsv/.travis.yml new file mode 100644 index 0000000..4f2ee4d --- /dev/null +++ b/vendor/github.com/gocarina/gocsv/.travis.yml @@ -0,0 +1 @@ +language: go diff --git a/vendor/github.com/gocarina/gocsv/README.md b/vendor/github.com/gocarina/gocsv/README.md new file mode 100644 index 0000000..69aabca --- /dev/null +++ b/vendor/github.com/gocarina/gocsv/README.md @@ -0,0 +1,151 @@ +Go CSV +===== + +The GoCSV package aims to provide easy serialization and deserialization functions to use CSV in Golang + +API and techniques inspired from https://godoc.org/gopkg.in/mgo.v2 + +[](https://godoc.org/github.com/gocarina/gocsv) +[](https://travis-ci.org/gocarina/gocsv) + +Installation +===== + +```go get -u github.com/gocarina/gocsv``` + +Full example +===== + +Consider the following CSV file + +```csv + +client_id,client_name,client_age +1,Jose,42 +2,Daniel,26 +3,Vincent,32 + +``` + +Easy binding in Go! +--- + +```go + +package main + +import ( + "fmt" + "gocsv" + "os" +) + +type Client struct { // Our example struct, you can use "-" to ignore a field + Id string `csv:"id"` + Name string `csv:"name"` + Age string `csv:"age"` + NotUsed string `csv:"-"` +} + +func main() { + clientsFile, err := os.OpenFile("clients.csv", os.O_RDWR|os.O_CREATE, os.ModePerm) + if err != nil { + panic(err) + } + defer clientsFile.Close() + + clients := []*Client{} + + if err := gocsv.UnmarshalFile(clientsFile, &clients); err != nil { // Load clients from file + panic(err) + } + for _, client := range clients { + fmt.Println("Hello", client.Name) + } + + if _, err := clientsFile.Seek(0, 0); err != nil { // Go to the start of the file + panic(err) + } + + clients = append(clients, &Client{Id: "12", Name: "John", Age: "21"}) // Add clients + clients = append(clients, &Client{Id: "13", Name: "Fred"}) + clients = append(clients, &Client{Id: "14", Name: "James", Age: "32"}) + clients = append(clients, &Client{Id: "15", Name: "Danny"}) + csvContent, err := gocsv.MarshalString(&clients) // Get all clients as CSV string + //err = gocsv.MarshalFile(&clients, clientsFile) // Use this to save the CSV back to the file + if err != nil { + panic(err) + } + fmt.Println(csvContent) // Display all clients as CSV string + +} + +``` + +Customizable Converters +--- + +```go + +type DateTime struct { + time.Time +} + +// Convert the internal date as CSV string +func (date *DateTime) MarshalCSV() (string, error) { + return date.Time.Format("20060201"), nil +} + +// You could also use the standard Stringer interface +func (date *DateTime) String() (string) { + return date.String() // Redundant, just for example +} + +// Convert the CSV string as internal date +func (date *DateTime) UnmarshalCSV(csv string) (err error) { + date.Time, err = time.Parse("20060201", csv) + if err != nil { + return err + } + return nil +} + +type Client struct { // Our example struct with a custom type (DateTime) + Id string `csv:"id"` + Name string `csv:"name"` + Employed DateTime `csv:"employed"` +} + +``` + +Customizable CSV Reader / Writer +--- + +```go + +func main() { + ... + + gocsv.SetCSVReader(func(in io.Reader) *csv.Reader { + //return csv.NewReader(in) + return gocsv.LazyCSVReader(in) // Allows use of quotes in CSV + }) + + ... + + gocsv.UnmarshalFile(file, &clients) + + ... + + gocsv.SetCSVWriter(func(out io.Writer) *csv.Writer { + return csv.NewWriter(out) + }) + + ... + + gocsv.MarshalFile(&clients, file) + + ... +} + +``` diff --git a/vendor/github.com/gocarina/gocsv/csv.go b/vendor/github.com/gocarina/gocsv/csv.go index 98cd6df..8f1bbfd 100644 --- a/vendor/github.com/gocarina/gocsv/csv.go +++ b/vendor/github.com/gocarina/gocsv/csv.go @@ -163,19 +163,22 @@ func UnmarshalCSV(in CSVReader, out interface{}) error { // UnmarshalToChan parses the CSV from the reader and send each value in the chan c. // The channel must have a concrete type. -func UnmarshalToChan(in io.Reader, c interface{}) (err error) { +func UnmarshalToChan(in io.Reader, c interface{}) error { + if c == nil { + return fmt.Errorf("goscv: channel is %v", c) + } return readEach(newDecoder(in), c) } // UnmarshalStringToChan parses the CSV from the string and send each value in the chan c. // The channel must have a concrete type. -func UnmarshalStringToChan(in string, c interface{}) (err error) { +func UnmarshalStringToChan(in string, c interface{}) error { return UnmarshalToChan(strings.NewReader(in), c) } // UnmarshalBytesToChan parses the CSV from the bytes and send each value in the chan c. // The channel must have a concrete type. -func UnmarshalBytesToChan(in []byte, c interface{}) (err error) { +func UnmarshalBytesToChan(in []byte, c interface{}) error { return UnmarshalToChan(bytes.NewReader(in), c) } @@ -215,3 +218,25 @@ func UnmarshalBytesToCallback(in []byte, f interface{}) (err error) { func UnmarshalStringToCallback(in string, c interface{}) (err error) { return UnmarshalToCallback(strings.NewReader(in), c) } + +func CSVToMap(in io.Reader) (map[string]string, error) { + decoder := newDecoder(in) + header, err := decoder.getCSVRow() + if err != nil { + return nil, err + } + if len(header) != 2 { + return nil, fmt.Errorf("maps can only be created for csv of two columns") + } + m := make(map[string]string) + for { + line, err := decoder.getCSVRow() + if err == io.EOF { + break + } else if err != nil { + return nil, err + } + m[line[0]] = line[1] + } + return m, nil +}
\ No newline at end of file diff --git a/vendor/github.com/gocarina/gocsv/decode_test.go b/vendor/github.com/gocarina/gocsv/decode_test.go new file mode 100644 index 0000000..9e18853 --- /dev/null +++ b/vendor/github.com/gocarina/gocsv/decode_test.go @@ -0,0 +1,469 @@ +package gocsv + +import ( + "bytes" + "encoding/csv" + "io" + "reflect" + "strconv" + "strings" + "testing" +) + +func Test_readTo(t *testing.T) { + blah := 0 + sptr := "*string" + sptr2 := "" + b := bytes.NewBufferString(`foo,BAR,Baz,Blah,SPtr +f,1,baz,,*string +e,3,b,,`) + d := &decoder{in: b} + + var samples []Sample + if err := readTo(d, &samples); err != nil { + t.Fatal(err) + } + if len(samples) != 2 { + t.Fatalf("expected 2 sample instances, got %d", len(samples)) + } + + expected := Sample{Foo: "f", Bar: 1, Baz: "baz", Blah: &blah, SPtr: &sptr} + if !reflect.DeepEqual(expected, samples[0]) { + t.Fatalf("expected first sample %v, got %v", expected, samples[0]) + } + + expected = Sample{Foo: "e", Bar: 3, Baz: "b", Blah: &blah, SPtr: &sptr2} + if !reflect.DeepEqual(expected, samples[1]) { + t.Fatalf("expected second sample %v, got %v", expected, samples[1]) + } + + b = bytes.NewBufferString(`foo,BAR,Baz +f,1,baz +e,BAD_INPUT,b`) + d = &decoder{in: b} + samples = []Sample{} + err := readTo(d, &samples) + if err == nil { + t.Fatalf("Expected error from bad input, got: %+v", samples) + } + switch actualErr := err.(type) { + case *csv.ParseError: + if actualErr.Line != 3 { + t.Fatalf("Expected csv.ParseError on line 3, got: %d", actualErr.Line) + } + if actualErr.Column != 2 { + t.Fatalf("Expected csv.ParseError in column 2, got: %d", actualErr.Column) + } + default: + t.Fatalf("incorrect error type: %T", err) + } + +} + +func Test_readTo_complex_embed(t *testing.T) { + b := bytes.NewBufferString(`first,foo,BAR,Baz,last,abc +aa,bb,11,cc,dd,ee +ff,gg,22,hh,ii,jj`) + d := &decoder{in: b} + + var samples []SkipFieldSample + if err := readTo(d, &samples); err != nil { + t.Fatal(err) + } + if len(samples) != 2 { + t.Fatalf("expected 2 sample instances, got %d", len(samples)) + } + expected := SkipFieldSample{ + EmbedSample: EmbedSample{ + Qux: "aa", + Sample: Sample{ + Foo: "bb", + Bar: 11, + Baz: "cc", + }, + Quux: "dd", + }, + Corge: "ee", + } + if expected != samples[0] { + t.Fatalf("expected first sample %v, got %v", expected, samples[0]) + } + expected = SkipFieldSample{ + EmbedSample: EmbedSample{ + Qux: "ff", + Sample: Sample{ + Foo: "gg", + Bar: 22, + Baz: "hh", + }, + Quux: "ii", + }, + Corge: "jj", + } + if expected != samples[1] { + t.Fatalf("expected first sample %v, got %v", expected, samples[1]) + } +} + +func Test_readEach(t *testing.T) { + b := bytes.NewBufferString(`first,foo,BAR,Baz,last,abc +aa,bb,11,cc,dd,ee +ff,gg,22,hh,ii,jj`) + d := &decoder{in: b} + + c := make(chan SkipFieldSample) + var samples []SkipFieldSample + go func() { + if err := readEach(d, c); err != nil { + t.Fatal(err) + } + }() + for v := range c { + samples = append(samples, v) + } + if len(samples) != 2 { + t.Fatalf("expected 2 sample instances, got %d", len(samples)) + } + expected := SkipFieldSample{ + EmbedSample: EmbedSample{ + Qux: "aa", + Sample: Sample{ + Foo: "bb", + Bar: 11, + Baz: "cc", + }, + Quux: "dd", + }, + Corge: "ee", + } + if expected != samples[0] { + t.Fatalf("expected first sample %v, got %v", expected, samples[0]) + } + expected = SkipFieldSample{ + EmbedSample: EmbedSample{ + Qux: "ff", + Sample: Sample{ + Foo: "gg", + Bar: 22, + Baz: "hh", + }, + Quux: "ii", + }, + Corge: "jj", + } + if expected != samples[1] { + t.Fatalf("expected first sample %v, got %v", expected, samples[1]) + } +} + +func Test_maybeMissingStructFields(t *testing.T) { + structTags := []fieldInfo{ + {keys: []string{"foo"}}, + {keys: []string{"bar"}}, + {keys: []string{"baz"}}, + } + badHeaders := []string{"hi", "mom", "bacon"} + goodHeaders := []string{"foo", "bar", "baz"} + + // no tags to match, expect no error + if err := maybeMissingStructFields([]fieldInfo{}, goodHeaders); err != nil { + t.Fatal(err) + } + + // bad headers, expect an error + if err := maybeMissingStructFields(structTags, badHeaders); err == nil { + t.Fatal("expected an error, but no error found") + } + + // good headers, expect no error + if err := maybeMissingStructFields(structTags, goodHeaders); err != nil { + t.Fatal(err) + } + + // extra headers, but all structtags match; expect no error + moarHeaders := append(goodHeaders, "qux", "quux", "corge", "grault") + if err := maybeMissingStructFields(structTags, moarHeaders); err != nil { + t.Fatal(err) + } + + // not all structTags match, but there's plenty o' headers; expect + // error + mismatchedHeaders := []string{"foo", "qux", "quux", "corgi"} + if err := maybeMissingStructFields(structTags, mismatchedHeaders); err == nil { + t.Fatal("expected an error, but no error found") + } +} + +func Test_maybeDoubleHeaderNames(t *testing.T) { + b := bytes.NewBufferString(`foo,BAR,foo +f,1,baz +e,3,b`) + d := &decoder{in: b} + var samples []Sample + + // *** check maybeDoubleHeaderNames + if err := maybeDoubleHeaderNames([]string{"foo", "BAR", "foo"}); err == nil { + t.Fatal("maybeDoubleHeaderNames did not raise an error when a should have.") + } + + // *** check readTo + if err := readTo(d, &samples); err != nil { + t.Fatal(err) + } + // Double header allowed, value should be of third row + if samples[0].Foo != "baz" { + t.Fatal("Double header allowed, value should be of third row but is not. Function called is readTo.") + } + + b = bytes.NewBufferString(`foo,BAR,foo +f,1,baz +e,3,b`) + d = &decoder{in: b} + ShouldAlignDuplicateHeadersWithStructFieldOrder = true + if err := readTo(d, &samples); err != nil { + t.Fatal(err) + } + // Double header allowed, value should be of first row + if samples[0].Foo != "f" { + t.Fatal("Double header allowed, value should be of first row but is not. Function called is readTo.") + } + + ShouldAlignDuplicateHeadersWithStructFieldOrder = false + // Double header not allowed, should fail + FailIfDoubleHeaderNames = true + if err := readTo(d, &samples); err == nil { + t.Fatal("Double header not allowed but no error raised. Function called is readTo.") + } + + // *** check readEach + FailIfDoubleHeaderNames = false + b = bytes.NewBufferString(`foo,BAR,foo +f,1,baz +e,3,b`) + d = &decoder{in: b} + samples = samples[:0] + c := make(chan Sample) + go func() { + if err := readEach(d, c); err != nil { + t.Fatal(err) + } + }() + for v := range c { + samples = append(samples, v) + } + // Double header allowed, value should be of third row + if samples[0].Foo != "baz" { + t.Fatal("Double header allowed, value should be of third row but is not. Function called is readEach.") + } + // Double header not allowed, should fail + FailIfDoubleHeaderNames = true + b = bytes.NewBufferString(`foo,BAR,foo +f,1,baz +e,3,b`) + d = &decoder{in: b} + c = make(chan Sample) + go func() { + if err := readEach(d, c); err == nil { + t.Fatal("Double header not allowed but no error raised. Function called is readEach.") + } + }() + for v := range c { + samples = append(samples, v) + } +} + +func TestUnmarshalToCallback(t *testing.T) { + b := bytes.NewBufferString(`first,foo,BAR,Baz,last,abc +aa,bb,11,cc,dd,ee +ff,gg,22,hh,ii,jj`) + var samples []SkipFieldSample + if err := UnmarshalBytesToCallback(b.Bytes(), func(s SkipFieldSample) { + samples = append(samples, s) + }); err != nil { + t.Fatal(err) + } + if len(samples) != 2 { + t.Fatalf("expected 2 sample instances, got %d", len(samples)) + } + expected := SkipFieldSample{ + EmbedSample: EmbedSample{ + Qux: "aa", + Sample: Sample{ + Foo: "bb", + Bar: 11, + Baz: "cc", + }, + Quux: "dd", + }, + Corge: "ee", + } + if expected != samples[0] { + t.Fatalf("expected first sample %v, got %v", expected, samples[0]) + } + expected = SkipFieldSample{ + EmbedSample: EmbedSample{ + Qux: "ff", + Sample: Sample{ + Foo: "gg", + Bar: 22, + Baz: "hh", + }, + Quux: "ii", + }, + Corge: "jj", + } + if expected != samples[1] { + t.Fatalf("expected first sample %v, got %v", expected, samples[1]) + } +} + +// TestRenamedTypes tests for unmarshaling functions on redefined basic types. +func TestRenamedTypesUnmarshal(t *testing.T) { + b := bytes.NewBufferString(`foo;bar +1,4;1.5 +2,3;2.4`) + d := &decoder{in: b} + var samples []RenamedSample + + // Set different csv field separator to enable comma in floats + SetCSVReader(func(in io.Reader) *csv.Reader { + csvin := csv.NewReader(in) + csvin.Comma = ';' + return csvin + }) + // Switch back to default for tests executed after this + defer SetCSVReader(DefaultCSVReader) + + if err := readTo(d, &samples); err != nil { + t.Fatal(err) + } + if samples[0].RenamedFloatUnmarshaler != 1.4 { + t.Fatalf("Parsed float value wrong for renamed float64 type. Expected 1.4, got %v.", samples[0].RenamedFloatUnmarshaler) + } + if samples[0].RenamedFloatDefault != 1.5 { + t.Fatalf("Parsed float value wrong for renamed float64 type without an explicit unmarshaler function. Expected 1.5, got %v.", samples[0].RenamedFloatDefault) + } + + // Test that errors raised by UnmarshalCSV are correctly reported + b = bytes.NewBufferString(`foo;bar +4.2;2.4`) + d = &decoder{in: b} + samples = samples[:0] + if perr, _ := readTo(d, &samples).(*csv.ParseError); perr == nil { + t.Fatalf("Expected ParseError, got nil.") + } else if _, ok := perr.Err.(UnmarshalError); !ok { + t.Fatalf("Expected UnmarshalError, got %v", perr.Err) + } +} + +func (rf *RenamedFloat64Unmarshaler) UnmarshalCSV(csv string) (err error) { + // Purely for testing purposes: Raise error on specific string + if csv == "4.2" { + return UnmarshalError{"Test error: Invalid float 4.2"} + } + + // Convert , to . before parsing to create valid float strings + converted := strings.Replace(csv, ",", ".", -1) + var f float64 + if f, err = strconv.ParseFloat(converted, 64); err != nil { + return err + } + *rf = RenamedFloat64Unmarshaler(f) + return nil +} + +type UnmarshalError struct { + msg string +} + +func (e UnmarshalError) Error() string { + return e.msg +} + +func TestMultipleStructTags(t *testing.T) { + b := bytes.NewBufferString(`foo,BAR,Baz +e,3,b`) + d := &decoder{in: b} + + var samples []MultiTagSample + if err := readTo(d, &samples); err != nil { + t.Fatal(err) + } + if samples[0].Foo != "b" { + t.Fatalf("expected second tag value 'b' in multi tag struct field, got %v", samples[0].Foo) + } + + b = bytes.NewBufferString(`foo,BAR +e,3`) + d = &decoder{in: b} + + if err := readTo(d, &samples); err != nil { + t.Fatal(err) + } + if samples[0].Foo != "e" { + t.Fatalf("wrong value in multi tag struct field, expected 'e', got %v", samples[0].Foo) + } + + b = bytes.NewBufferString(`BAR,Baz +3,b`) + d = &decoder{in: b} + + if err := readTo(d, &samples); err != nil { + t.Fatal(err) + } + if samples[0].Foo != "b" { + t.Fatal("wrong value in multi tag struct field") + } +} + +func TestStructTagSeparator(t *testing.T) { + b := bytes.NewBufferString(`foo,BAR,Baz +e,3,b`) + d := &decoder{in: b} + + defaultTagSeparator := TagSeparator + TagSeparator = "|" + defer func() { TagSeparator = defaultTagSeparator }() + + var samples []TagSeparatorSample + if err := readTo(d, &samples); err != nil { + t.Fatal(err) + } + + if samples[0].Foo != "b" { + t.Fatal("expected second tag value in multi tag struct field.") + } +} + +func TestCSVToMap(t *testing.T) { + b := bytes.NewBufferString(`foo,BAR +4,Jose +2,Daniel +5,Vincent`) + m, err := CSVToMap(bytes.NewReader(b.Bytes())) + if err != nil { + t.Fatal(err) + } + if m["4"] != "Jose" { + t.Fatal("Expected Jose got", m["4"]) + } + if m["2"] != "Daniel" { + t.Fatal("Expected Daniel got", m["2"]) + } + if m["5"] != "Vincent" { + t.Fatal("Expected Vincent got", m["5"]) + } + + b = bytes.NewBufferString(`foo,BAR,Baz +e,3,b`) + _, err = CSVToMap(bytes.NewReader(b.Bytes())) + if err == nil { + t.Fatal("Something went wrong") + } + b = bytes.NewBufferString(`foo +e`) + _, err = CSVToMap(bytes.NewReader(b.Bytes())) + if err == nil { + t.Fatal("Something went wrong") + } +}
\ No newline at end of file diff --git a/vendor/github.com/gocarina/gocsv/encode.go b/vendor/github.com/gocarina/gocsv/encode.go index 4a87462..6e4e67b 100644 --- a/vendor/github.com/gocarina/gocsv/encode.go +++ b/vendor/github.com/gocarina/gocsv/encode.go @@ -17,7 +17,10 @@ func newEncoder(out io.Writer) *encoder { func writeFromChan(writer *csv.Writer, c <-chan interface{}) error { // Get the first value. It wil determine the header structure. - firstValue := <-c + firstValue, ok := <-c + if !ok { + return fmt.Errorf("channel is closed") + } inValue, inType := getConcreteReflectValueAndType(firstValue) // Get the concrete type if err := ensureStructOrPtr(inType); err != nil { return err diff --git a/vendor/github.com/gocarina/gocsv/encode_test.go b/vendor/github.com/gocarina/gocsv/encode_test.go new file mode 100644 index 0000000..3a18086 --- /dev/null +++ b/vendor/github.com/gocarina/gocsv/encode_test.go @@ -0,0 +1,265 @@ +package gocsv + +import ( + "bytes" + "encoding/csv" + "io" + "math" + "strconv" + "strings" + "testing" +) + +func assertLine(t *testing.T, expected, actual []string) { + if len(expected) != len(actual) { + t.Fatalf("line length mismatch between expected: %d and actual: %d", len(expected), len(actual)) + } + for i := range expected { + if expected[i] != actual[i] { + t.Fatalf("mismatch on field %d at line `%s`: %s != %s", i, expected, expected[i], actual[i]) + } + } +} + +func Test_writeTo(t *testing.T) { + b := bytes.Buffer{} + e := &encoder{out: &b} + blah := 2 + sptr := "*string" + s := []Sample{ + {Foo: "f", Bar: 1, Baz: "baz", Frop: 0.1, Blah: &blah, SPtr: &sptr}, + {Foo: "e", Bar: 3, Baz: "b", Frop: 6.0 / 13, Blah: nil, SPtr: nil}, + } + if err := writeTo(csv.NewWriter(e.out), s, false); err != nil { + t.Fatal(err) + } + + lines, err := csv.NewReader(&b).ReadAll() + if err != nil { + t.Fatal(err) + } + if len(lines) != 3 { + t.Fatalf("expected 3 lines, got %d", len(lines)) + } + assertLine(t, []string{"foo", "BAR", "Baz", "Quux", "Blah", "SPtr"}, lines[0]) + assertLine(t, []string{"f", "1", "baz", "0.1", "2", "*string"}, lines[1]) + assertLine(t, []string{"e", "3", "b", "0.46153846153846156", "", ""}, lines[2]) +} + +func Test_writeTo_NoHeaders(t *testing.T) { + b := bytes.Buffer{} + e := &encoder{out: &b} + blah := 2 + sptr := "*string" + s := []Sample{ + {Foo: "f", Bar: 1, Baz: "baz", Frop: 0.1, Blah: &blah, SPtr: &sptr}, + {Foo: "e", Bar: 3, Baz: "b", Frop: 6.0 / 13, Blah: nil, SPtr: nil}, + } + if err := writeTo(csv.NewWriter(e.out), s, true); err != nil { + t.Fatal(err) + } + + lines, err := csv.NewReader(&b).ReadAll() + if err != nil { + t.Fatal(err) + } + if len(lines) != 2 { + t.Fatalf("expected 2 lines, got %d", len(lines)) + } + assertLine(t, []string{"f", "1", "baz", "0.1", "2", "*string"}, lines[0]) + assertLine(t, []string{"e", "3", "b", "0.46153846153846156", "", ""}, lines[1]) +} + +func Test_writeTo_multipleTags(t *testing.T) { + b := bytes.Buffer{} + e := &encoder{out: &b} + s := []MultiTagSample{ + {Foo: "abc", Bar: 123}, + {Foo: "def", Bar: 234}, + } + if err := writeTo(csv.NewWriter(e.out), s, false); err != nil { + t.Fatal(err) + } + + lines, err := csv.NewReader(&b).ReadAll() + if err != nil { + t.Fatal(err) + } + if len(lines) != 3 { + t.Fatalf("expected 3 lines, got %d", len(lines)) + } + // the first tag for each field is the encoding CSV header + assertLine(t, []string{"Baz", "BAR"}, lines[0]) + assertLine(t, []string{"abc", "123"}, lines[1]) + assertLine(t, []string{"def", "234"}, lines[2]) +} + +func Test_writeTo_embed(t *testing.T) { + b := bytes.Buffer{} + e := &encoder{out: &b} + blah := 2 + sptr := "*string" + s := []EmbedSample{ + { + Qux: "aaa", + Sample: Sample{Foo: "f", Bar: 1, Baz: "baz", Frop: 0.2, Blah: &blah, SPtr: &sptr}, + Ignore: "shouldn't be marshalled", + Quux: "zzz", + Grault: math.Pi, + }, + } + if err := writeTo(csv.NewWriter(e.out), s, false); err != nil { + t.Fatal(err) + } + + lines, err := csv.NewReader(&b).ReadAll() + if err != nil { + t.Fatal(err) + } + if len(lines) != 2 { + t.Fatalf("expected 2 lines, got %d", len(lines)) + } + assertLine(t, []string{"first", "foo", "BAR", "Baz", "Quux", "Blah", "SPtr", "garply", "last"}, lines[0]) + assertLine(t, []string{"aaa", "f", "1", "baz", "0.2", "2", "*string", "3.141592653589793", "zzz"}, lines[1]) +} + +func Test_writeTo_complex_embed(t *testing.T) { + b := bytes.Buffer{} + e := &encoder{out: &b} + sptr := "*string" + sfs := []SkipFieldSample{ + { + EmbedSample: EmbedSample{ + Qux: "aaa", + Sample: Sample{ + Foo: "bbb", + Bar: 111, + Baz: "ddd", + Frop: 1.2e22, + Blah: nil, + SPtr: &sptr, + }, + Ignore: "eee", + Grault: 0.1, + Quux: "fff", + }, + MoreIgnore: "ggg", + Corge: "hhh", + }, + } + if err := writeTo(csv.NewWriter(e.out), sfs, false); err != nil { + t.Fatal(err) + } + lines, err := csv.NewReader(&b).ReadAll() + if err != nil { + t.Fatal(err) + } + if len(lines) != 2 { + t.Fatalf("expected 2 lines, got %d", len(lines)) + } + assertLine(t, []string{"first", "foo", "BAR", "Baz", "Quux", "Blah", "SPtr", "garply", "last", "abc"}, lines[0]) + assertLine(t, []string{"aaa", "bbb", "111", "ddd", "12000000000000000000000", "", "*string", "0.1", "fff", "hhh"}, lines[1]) +} + +func Test_writeToChan(t *testing.T) { + b := bytes.Buffer{} + e := &encoder{out: &b} + c := make(chan interface{}) + sptr := "*string" + go func() { + for i := 0; i < 100; i++ { + v := Sample{Foo: "f", Bar: i, Baz: "baz" + strconv.Itoa(i), Frop: float64(i), Blah: nil, SPtr: &sptr} + c <- v + } + close(c) + }() + if err := MarshalChan(c, csv.NewWriter(e.out)); err != nil { + t.Fatal(err) + } + lines, err := csv.NewReader(&b).ReadAll() + if err != nil { + t.Fatal(err) + } + if len(lines) != 101 { + t.Fatalf("expected 100 lines, got %d", len(lines)) + } + for i, l := range lines { + if i == 0 { + assertLine(t, []string{"foo", "BAR", "Baz", "Quux", "Blah", "SPtr"}, l) + continue + } + assertLine(t, []string{"f", strconv.Itoa(i - 1), "baz" + strconv.Itoa(i-1), strconv.FormatFloat(float64(i-1), 'f', -1, 64), "", "*string"}, l) + } +} + +// TestRenamedTypes tests for marshaling functions on redefined basic types. +func TestRenamedTypesMarshal(t *testing.T) { + samples := []RenamedSample{ + {RenamedFloatUnmarshaler: 1.4, RenamedFloatDefault: 1.5}, + {RenamedFloatUnmarshaler: 2.3, RenamedFloatDefault: 2.4}, + } + + SetCSVWriter(func(out io.Writer) *csv.Writer { + csvout := csv.NewWriter(out) + csvout.Comma = ';' + return csvout + }) + // Switch back to default for tests executed after this + defer SetCSVWriter(DefaultCSVWriter) + + csvContent, err := MarshalString(&samples) + if err != nil { + t.Fatal(err) + } + if csvContent != "foo;bar\n1,4;1.5\n2,3;2.4\n" { + t.Fatalf("Error marshaling floats with , as separator. Expected \nfoo;bar\n1,4;1.5\n2,3;2.4\ngot:\n%v", csvContent) + } + + // Test that errors raised by MarshalCSV are correctly reported + samples = []RenamedSample{ + {RenamedFloatUnmarshaler: 4.2, RenamedFloatDefault: 1.5}, + } + _, err = MarshalString(&samples) + if _, ok := err.(MarshalError); !ok { + t.Fatalf("Expected UnmarshalError, got %v", err) + } +} + +// TestCustomTagSeparatorMarshal tests for custom tag separator in marshalling. +func TestCustomTagSeparatorMarshal(t *testing.T) { + samples := []RenamedSample{ + {RenamedFloatUnmarshaler: 1.4, RenamedFloatDefault: 1.5}, + {RenamedFloatUnmarshaler: 2.3, RenamedFloatDefault: 2.4}, + } + + TagSeparator = " | " + // Switch back to default TagSeparator after this + defer func () { + TagSeparator = "," + }() + + csvContent, err := MarshalString(&samples) + if err != nil { + t.Fatal(err) + } + if csvContent != "foo|bar\n1,4|1.5\n2,3|2.4\n" { + t.Fatalf("Error marshaling floats with , as separator. Expected \nfoo|bar\n1,4|1.5\n2,3|2.4\ngot:\n%v", csvContent) + } +} + +func (rf *RenamedFloat64Unmarshaler) MarshalCSV() (csv string, err error) { + if *rf == RenamedFloat64Unmarshaler(4.2) { + return "", MarshalError{"Test error: Invalid float 4.2"} + } + csv = strconv.FormatFloat(float64(*rf), 'f', 1, 64) + csv = strings.Replace(csv, ".", ",", -1) + return csv, nil +} + +type MarshalError struct { + msg string +} + +func (e MarshalError) Error() string { + return e.msg +} diff --git a/vendor/github.com/gocarina/gocsv/sample_structs_test.go b/vendor/github.com/gocarina/gocsv/sample_structs_test.go new file mode 100644 index 0000000..f6d1427 --- /dev/null +++ b/vendor/github.com/gocarina/gocsv/sample_structs_test.go @@ -0,0 +1,43 @@ +package gocsv + +type Sample struct { + Foo string `csv:"foo"` + Bar int `csv:"BAR"` + Baz string `csv:"Baz"` + Frop float64 `csv:"Quux"` + Blah *int `csv:"Blah"` + SPtr *string `csv:"SPtr"` +} + +type EmbedSample struct { + Qux string `csv:"first"` + Sample + Ignore string `csv:"-"` + Grault float64 `csv:"garply"` + Quux string `csv:"last"` +} + +type SkipFieldSample struct { + EmbedSample + MoreIgnore string `csv:"-"` + Corge string `csv:"abc"` +} + +// Testtype for unmarshal/marshal functions on renamed basic types +type RenamedFloat64Unmarshaler float64 +type RenamedFloat64Default float64 + +type RenamedSample struct { + RenamedFloatUnmarshaler RenamedFloat64Unmarshaler `csv:"foo"` + RenamedFloatDefault RenamedFloat64Default `csv:"bar"` +} + +type MultiTagSample struct { + Foo string `csv:"Baz,foo"` + Bar int `csv:"BAR"` +} + +type TagSeparatorSample struct { + Foo string `csv:"Baz|foo"` + Bar int `csv:"BAR"` +} diff --git a/vendor/github.com/gocarina/gocsv/types.go b/vendor/github.com/gocarina/gocsv/types.go index 281c806..6768256 100644 --- a/vendor/github.com/gocarina/gocsv/types.go +++ b/vendor/github.com/gocarina/gocsv/types.go @@ -400,7 +400,6 @@ func unmarshall(field reflect.Value, value string) error { dupField = reflect.New(field.Type().Elem()) field.Set(dupField) return unMarshallIt(dupField) - break } dupField = dupField.Elem() } |
