summaryrefslogtreecommitdiff
path: root/vendor/github.com/gocarina
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/gocarina')
-rw-r--r--vendor/github.com/gocarina/gocsv/.travis.yml1
-rw-r--r--vendor/github.com/gocarina/gocsv/README.md151
-rw-r--r--vendor/github.com/gocarina/gocsv/csv.go31
-rw-r--r--vendor/github.com/gocarina/gocsv/decode_test.go469
-rw-r--r--vendor/github.com/gocarina/gocsv/encode.go5
-rw-r--r--vendor/github.com/gocarina/gocsv/encode_test.go265
-rw-r--r--vendor/github.com/gocarina/gocsv/sample_structs_test.go43
-rw-r--r--vendor/github.com/gocarina/gocsv/types.go1
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
+
+[![GoDoc](https://godoc.org/github.com/gocarina/gocsv?status.png)](https://godoc.org/github.com/gocarina/gocsv)
+[![Build Status](https://travis-ci.org/gocarina/gocsv.svg?branch=master)](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()
}