summaryrefslogtreecommitdiff
path: root/vendor/github.com/gocarina/gocsv
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/gocarina/gocsv')
-rw-r--r--vendor/github.com/gocarina/gocsv/.gitignore1
-rw-r--r--vendor/github.com/gocarina/gocsv/LICENSE21
-rw-r--r--vendor/github.com/gocarina/gocsv/README.md170
-rw-r--r--vendor/github.com/gocarina/gocsv/csv.go319
-rw-r--r--vendor/github.com/gocarina/gocsv/decode.go326
-rw-r--r--vendor/github.com/gocarina/gocsv/encode.go139
-rw-r--r--vendor/github.com/gocarina/gocsv/reflect.go107
-rw-r--r--vendor/github.com/gocarina/gocsv/safe_csv.go32
-rw-r--r--vendor/github.com/gocarina/gocsv/types.go456
-rw-r--r--vendor/github.com/gocarina/gocsv/unmarshaller.go97
10 files changed, 1668 insertions, 0 deletions
diff --git a/vendor/github.com/gocarina/gocsv/.gitignore b/vendor/github.com/gocarina/gocsv/.gitignore
new file mode 100644
index 0000000..485dee6
--- /dev/null
+++ b/vendor/github.com/gocarina/gocsv/.gitignore
@@ -0,0 +1 @@
+.idea
diff --git a/vendor/github.com/gocarina/gocsv/LICENSE b/vendor/github.com/gocarina/gocsv/LICENSE
new file mode 100644
index 0000000..052a371
--- /dev/null
+++ b/vendor/github.com/gocarina/gocsv/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Jonathan Picques
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE. \ No newline at end of file
diff --git a/vendor/github.com/gocarina/gocsv/README.md b/vendor/github.com/gocarina/gocsv/README.md
new file mode 100644
index 0000000..c3fae62
--- /dev/null
+++ b/vendor/github.com/gocarina/gocsv/README.md
@@ -0,0 +1,170 @@
+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:"client_id"`
+ Name string `csv:"client_name"`
+ Age string `csv:"client_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) gocsv.CSVReader {
+ r := csv.NewReader(in)
+ r.Comma = '|'
+ return r // Allows use pipe as delimiter
+ })
+
+ ...
+
+ gocsv.SetCSVReader(func(in io.Reader) gocsv.CSVReader {
+ r := csv.NewReader(in)
+ r.LazyQuotes = true
+ r.Comma = '.'
+ return r // Allows use dot as delimiter and use quotes in CSV
+ })
+
+ ...
+
+ gocsv.SetCSVReader(func(in io.Reader) gocsv.CSVReader {
+ //return csv.NewReader(in)
+ return gocsv.LazyCSVReader(in) // Allows use of quotes in CSV
+ })
+
+ ...
+
+ gocsv.UnmarshalFile(file, &clients)
+
+ ...
+
+ gocsv.SetCSVWriter(func(out io.Writer) *SafeCSVWriter {
+ writer := csv.NewWriter(out)
+ writer.Comma = '|'
+ return gocsv.NewSafeCSVWriter(writer)
+ })
+
+ ...
+
+ gocsv.MarshalFile(&clients, file)
+
+ ...
+}
+
+```
diff --git a/vendor/github.com/gocarina/gocsv/csv.go b/vendor/github.com/gocarina/gocsv/csv.go
new file mode 100644
index 0000000..2e336ea
--- /dev/null
+++ b/vendor/github.com/gocarina/gocsv/csv.go
@@ -0,0 +1,319 @@
+// Copyright 2014 Jonathan Picques. All rights reserved.
+// Use of this source code is governed by a MIT license
+// The license can be found in the LICENSE file.
+
+// The GoCSV package aims to provide easy CSV serialization and deserialization to the golang programming language
+
+package gocsv
+
+import (
+ "bytes"
+ "encoding/csv"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "strings"
+)
+
+// FailIfUnmatchedStructTags indicates whether it is considered an error when there is an unmatched
+// struct tag.
+var FailIfUnmatchedStructTags = false
+
+// FailIfDoubleHeaderNames indicates whether it is considered an error when a header name is repeated
+// in the csv header.
+var FailIfDoubleHeaderNames = false
+
+// ShouldAlignDuplicateHeadersWithStructFieldOrder indicates whether we should align duplicate CSV
+// headers per their alignment in the struct definition.
+var ShouldAlignDuplicateHeadersWithStructFieldOrder = false
+
+// TagSeparator defines seperator string for multiple csv tags in struct fields
+var TagSeparator = ","
+
+// --------------------------------------------------------------------------
+// CSVWriter used to format CSV
+
+var selfCSVWriter = DefaultCSVWriter
+
+// DefaultCSVWriter is the default SafeCSVWriter used to format CSV (cf. csv.NewWriter)
+func DefaultCSVWriter(out io.Writer) *SafeCSVWriter {
+ writer := NewSafeCSVWriter(csv.NewWriter(out))
+
+ // As only one rune can be defined as a CSV separator, we are going to trim
+ // the custom tag separator and use the first rune.
+ if runes := []rune(strings.TrimSpace(TagSeparator)); len(runes) > 0 {
+ writer.Comma = runes[0]
+ }
+
+ return writer
+}
+
+// SetCSVWriter sets the SafeCSVWriter used to format CSV.
+func SetCSVWriter(csvWriter func(io.Writer) *SafeCSVWriter) {
+ selfCSVWriter = csvWriter
+}
+
+func getCSVWriter(out io.Writer) *SafeCSVWriter {
+ return selfCSVWriter(out)
+}
+
+// --------------------------------------------------------------------------
+// CSVReader used to parse CSV
+
+var selfCSVReader = DefaultCSVReader
+
+// DefaultCSVReader is the default CSV reader used to parse CSV (cf. csv.NewReader)
+func DefaultCSVReader(in io.Reader) CSVReader {
+ return csv.NewReader(in)
+}
+
+// LazyCSVReader returns a lazy CSV reader, with LazyQuotes and TrimLeadingSpace.
+func LazyCSVReader(in io.Reader) CSVReader {
+ csvReader := csv.NewReader(in)
+ csvReader.LazyQuotes = true
+ csvReader.TrimLeadingSpace = true
+ return csvReader
+}
+
+// SetCSVReader sets the CSV reader used to parse CSV.
+func SetCSVReader(csvReader func(io.Reader) CSVReader) {
+ selfCSVReader = csvReader
+}
+
+func getCSVReader(in io.Reader) CSVReader {
+ return selfCSVReader(in)
+}
+
+// --------------------------------------------------------------------------
+// Marshal functions
+
+// MarshalFile saves the interface as CSV in the file.
+func MarshalFile(in interface{}, file *os.File) (err error) {
+ return Marshal(in, file)
+}
+
+// MarshalString returns the CSV string from the interface.
+func MarshalString(in interface{}) (out string, err error) {
+ bufferString := bytes.NewBufferString(out)
+ if err := Marshal(in, bufferString); err != nil {
+ return "", err
+ }
+ return bufferString.String(), nil
+}
+
+// MarshalBytes returns the CSV bytes from the interface.
+func MarshalBytes(in interface{}) (out []byte, err error) {
+ bufferString := bytes.NewBuffer(out)
+ if err := Marshal(in, bufferString); err != nil {
+ return nil, err
+ }
+ return bufferString.Bytes(), nil
+}
+
+// Marshal returns the CSV in writer from the interface.
+func Marshal(in interface{}, out io.Writer) (err error) {
+ writer := getCSVWriter(out)
+ return writeTo(writer, in, false)
+}
+
+// Marshal returns the CSV in writer from the interface.
+func MarshalWithoutHeaders(in interface{}, out io.Writer) (err error) {
+ writer := getCSVWriter(out)
+ return writeTo(writer, in, true)
+}
+
+// MarshalChan returns the CSV read from the channel.
+func MarshalChan(c <-chan interface{}, out *SafeCSVWriter) error {
+ return writeFromChan(out, c)
+}
+
+// MarshalCSV returns the CSV in writer from the interface.
+func MarshalCSV(in interface{}, out *SafeCSVWriter) (err error) {
+ return writeTo(out, in, false)
+}
+
+// MarshalCSVWithoutHeaders returns the CSV in writer from the interface.
+func MarshalCSVWithoutHeaders(in interface{}, out *SafeCSVWriter) (err error) {
+ return writeTo(out, in, true)
+}
+
+// --------------------------------------------------------------------------
+// Unmarshal functions
+
+// UnmarshalFile parses the CSV from the file in the interface.
+func UnmarshalFile(in *os.File, out interface{}) error {
+ return Unmarshal(in, out)
+}
+
+// UnmarshalString parses the CSV from the string in the interface.
+func UnmarshalString(in string, out interface{}) error {
+ return Unmarshal(strings.NewReader(in), out)
+}
+
+// UnmarshalBytes parses the CSV from the bytes in the interface.
+func UnmarshalBytes(in []byte, out interface{}) error {
+ return Unmarshal(bytes.NewReader(in), out)
+}
+
+// Unmarshal parses the CSV from the reader in the interface.
+func Unmarshal(in io.Reader, out interface{}) error {
+ return readTo(newDecoder(in), out)
+}
+
+// UnmarshalDecoder parses the CSV from the decoder in the interface
+func UnmarshalDecoder(in Decoder, out interface{}) error {
+ return readTo(in, out)
+}
+
+// UnmarshalCSV parses the CSV from the reader in the interface.
+func UnmarshalCSV(in CSVReader, out interface{}) error {
+ return readTo(csvDecoder{in}, out)
+}
+
+// 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{}) error {
+ if c == nil {
+ return fmt.Errorf("goscv: channel is %v", c)
+ }
+ return readEach(newDecoder(in), c)
+}
+
+// UnmarshalDecoderToChan parses the CSV from the decoder and send each value in the chan c.
+// The channel must have a concrete type.
+func UnmarshalDecoderToChan(in SimpleDecoder, c interface{}) error {
+ if c == nil {
+ return fmt.Errorf("goscv: channel is %v", c)
+ }
+ return readEach(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{}) 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{}) error {
+ return UnmarshalToChan(bytes.NewReader(in), c)
+}
+
+// UnmarshalToCallback parses the CSV from the reader and send each value to the given func f.
+// The func must look like func(Struct).
+func UnmarshalToCallback(in io.Reader, f interface{}) error {
+ valueFunc := reflect.ValueOf(f)
+ t := reflect.TypeOf(f)
+ if t.NumIn() != 1 {
+ return fmt.Errorf("the given function must have exactly one parameter")
+ }
+ cerr := make(chan error)
+ c := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, t.In(0)), 0)
+ go func() {
+ cerr <- UnmarshalToChan(in, c.Interface())
+ }()
+ for {
+ select {
+ case err := <-cerr:
+ return err
+ default:
+ }
+ v, notClosed := c.Recv()
+ if !notClosed || v.Interface() == nil {
+ break
+ }
+ valueFunc.Call([]reflect.Value{v})
+ }
+ return nil
+}
+
+// UnmarshalDecoderToCallback parses the CSV from the decoder and send each value to the given func f.
+// The func must look like func(Struct).
+func UnmarshalDecoderToCallback(in SimpleDecoder, f interface{}) error {
+ valueFunc := reflect.ValueOf(f)
+ t := reflect.TypeOf(f)
+ if t.NumIn() != 1 {
+ return fmt.Errorf("the given function must have exactly one parameter")
+ }
+ cerr := make(chan error)
+ c := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, t.In(0)), 0)
+ go func() {
+ cerr <- UnmarshalDecoderToChan(in, c.Interface())
+ }()
+ for {
+ select {
+ case err := <-cerr:
+ return err
+ default:
+ }
+ v, notClosed := c.Recv()
+ if !notClosed || v.Interface() == nil {
+ break
+ }
+ valueFunc.Call([]reflect.Value{v})
+ }
+ return nil
+}
+
+// UnmarshalBytesToCallback parses the CSV from the bytes and send each value to the given func f.
+// The func must look like func(Struct).
+func UnmarshalBytesToCallback(in []byte, f interface{}) error {
+ return UnmarshalToCallback(bytes.NewReader(in), f)
+}
+
+// UnmarshalStringToCallback parses the CSV from the string and send each value to the given func f.
+// The func must look like func(Struct).
+func UnmarshalStringToCallback(in string, c interface{}) (err error) {
+ return UnmarshalToCallback(strings.NewReader(in), c)
+}
+
+// CSVToMap creates a simple map from a CSV of 2 columns.
+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
+}
+
+// CSVToMaps takes a reader and returns an array of dictionaries, using the header row as the keys
+func CSVToMaps(reader io.Reader) ([]map[string]string, error) {
+ r := csv.NewReader(reader)
+ rows := []map[string]string{}
+ var header []string
+ for {
+ record, err := r.Read()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ return nil, err
+ }
+ if header == nil {
+ header = record
+ } else {
+ dict := map[string]string{}
+ for i := range header {
+ dict[header[i]] = record[i]
+ }
+ rows = append(rows, dict)
+ }
+ }
+ return rows, nil
+}
diff --git a/vendor/github.com/gocarina/gocsv/decode.go b/vendor/github.com/gocarina/gocsv/decode.go
new file mode 100644
index 0000000..980d7f7
--- /dev/null
+++ b/vendor/github.com/gocarina/gocsv/decode.go
@@ -0,0 +1,326 @@
+package gocsv
+
+import (
+ "encoding/csv"
+ "errors"
+ "fmt"
+ "io"
+ "reflect"
+)
+
+// Decoder .
+type Decoder interface {
+ getCSVRows() ([][]string, error)
+}
+
+// SimpleDecoder .
+type SimpleDecoder interface {
+ getCSVRow() ([]string, error)
+}
+
+type decoder struct {
+ in io.Reader
+ csvDecoder *csvDecoder
+}
+
+func newDecoder(in io.Reader) *decoder {
+ return &decoder{in: in}
+}
+
+func (decode *decoder) getCSVRows() ([][]string, error) {
+ return getCSVReader(decode.in).ReadAll()
+}
+
+func (decode *decoder) getCSVRow() ([]string, error) {
+ if decode.csvDecoder == nil {
+ decode.csvDecoder = &csvDecoder{getCSVReader(decode.in)}
+ }
+ return decode.csvDecoder.Read()
+}
+
+type CSVReader interface {
+ Read() ([]string, error)
+ ReadAll() ([][]string, error)
+}
+
+type csvDecoder struct {
+ CSVReader
+}
+
+func (c csvDecoder) getCSVRows() ([][]string, error) {
+ return c.ReadAll()
+}
+
+func (c csvDecoder) getCSVRow() ([]string, error) {
+ return c.Read()
+}
+
+func mismatchStructFields(structInfo []fieldInfo, headers []string) []string {
+ missing := make([]string, 0)
+ if len(structInfo) == 0 {
+ return missing
+ }
+
+ headerMap := make(map[string]struct{}, len(headers))
+ for idx := range headers {
+ headerMap[headers[idx]] = struct{}{}
+ }
+
+ for _, info := range structInfo {
+ found := false
+ for _, key := range info.keys {
+ if _, ok := headerMap[key]; ok {
+ found = true
+ break
+ }
+ }
+ if !found {
+ missing = append(missing, info.keys...)
+ }
+ }
+ return missing
+}
+
+func mismatchHeaderFields(structInfo []fieldInfo, headers []string) []string {
+ missing := make([]string, 0)
+ if len(headers) == 0 {
+ return missing
+ }
+
+ keyMap := make(map[string]struct{}, 0)
+ for _, info := range structInfo {
+ for _, key := range info.keys {
+ keyMap[key] = struct{}{}
+ }
+ }
+
+ for _, header := range headers {
+ if _, ok := keyMap[header]; !ok {
+ missing = append(missing, header)
+ }
+ }
+ return missing
+}
+
+func maybeMissingStructFields(structInfo []fieldInfo, headers []string) error {
+ missing := mismatchStructFields(structInfo, headers)
+ if len(missing) != 0 {
+ return fmt.Errorf("found unmatched struct field with tags %v", missing)
+ }
+ return nil
+}
+
+// Check that no header name is repeated twice
+func maybeDoubleHeaderNames(headers []string) error {
+ headerMap := make(map[string]bool, len(headers))
+ for _, v := range headers {
+ if _, ok := headerMap[v]; ok {
+ return fmt.Errorf("Repeated header name: %v", v)
+ }
+ headerMap[v] = true
+ }
+ return nil
+}
+
+func readTo(decoder Decoder, out interface{}) error {
+ outValue, outType := getConcreteReflectValueAndType(out) // Get the concrete type (not pointer) (Slice<?> or Array<?>)
+ if err := ensureOutType(outType); err != nil {
+ return err
+ }
+ outInnerWasPointer, outInnerType := getConcreteContainerInnerType(outType) // Get the concrete inner type (not pointer) (Container<"?">)
+ if err := ensureOutInnerType(outInnerType); err != nil {
+ return err
+ }
+ csvRows, err := decoder.getCSVRows() // Get the CSV csvRows
+ if err != nil {
+ return err
+ }
+ if len(csvRows) == 0 {
+ return errors.New("empty csv file given")
+ }
+ if err := ensureOutCapacity(&outValue, len(csvRows)); err != nil { // Ensure the container is big enough to hold the CSV content
+ return err
+ }
+ outInnerStructInfo := getStructInfo(outInnerType) // Get the inner struct info to get CSV annotations
+ if len(outInnerStructInfo.Fields) == 0 {
+ return errors.New("no csv struct tags found")
+ }
+
+ headers := csvRows[0]
+ body := csvRows[1:]
+
+ csvHeadersLabels := make(map[int]*fieldInfo, len(outInnerStructInfo.Fields)) // Used to store the correspondance header <-> position in CSV
+
+ headerCount := map[string]int{}
+ for i, csvColumnHeader := range headers {
+ curHeaderCount := headerCount[csvColumnHeader]
+ if fieldInfo := getCSVFieldPosition(csvColumnHeader, outInnerStructInfo, curHeaderCount); fieldInfo != nil {
+ csvHeadersLabels[i] = fieldInfo
+ if ShouldAlignDuplicateHeadersWithStructFieldOrder {
+ curHeaderCount++
+ headerCount[csvColumnHeader] = curHeaderCount
+ }
+ }
+ }
+
+ if FailIfUnmatchedStructTags {
+ if err := maybeMissingStructFields(outInnerStructInfo.Fields, headers); err != nil {
+ return err
+ }
+ }
+ if FailIfDoubleHeaderNames {
+ if err := maybeDoubleHeaderNames(headers); err != nil {
+ return err
+ }
+ }
+
+ for i, csvRow := range body {
+ outInner := createNewOutInner(outInnerWasPointer, outInnerType)
+ for j, csvColumnContent := range csvRow {
+ if fieldInfo, ok := csvHeadersLabels[j]; ok { // Position found accordingly to header name
+ if err := setInnerField(&outInner, outInnerWasPointer, fieldInfo.IndexChain, csvColumnContent, fieldInfo.omitEmpty); err != nil { // Set field of struct
+ return &csv.ParseError{
+ Line: i + 2, //add 2 to account for the header & 0-indexing of arrays
+ Column: j + 1,
+ Err: err,
+ }
+ }
+ }
+ }
+ outValue.Index(i).Set(outInner)
+ }
+ return nil
+}
+
+func readEach(decoder SimpleDecoder, c interface{}) error {
+ headers, err := decoder.getCSVRow()
+ if err != nil {
+ return err
+ }
+ outValue, outType := getConcreteReflectValueAndType(c) // Get the concrete type (not pointer) (Slice<?> or Array<?>)
+ if err := ensureOutType(outType); err != nil {
+ return err
+ }
+ defer outValue.Close()
+ outInnerWasPointer, outInnerType := getConcreteContainerInnerType(outType) // Get the concrete inner type (not pointer) (Container<"?">)
+ if err := ensureOutInnerType(outInnerType); err != nil {
+ return err
+ }
+ outInnerStructInfo := getStructInfo(outInnerType) // Get the inner struct info to get CSV annotations
+ if len(outInnerStructInfo.Fields) == 0 {
+ return errors.New("no csv struct tags found")
+ }
+ csvHeadersLabels := make(map[int]*fieldInfo, len(outInnerStructInfo.Fields)) // Used to store the correspondance header <-> position in CSV
+ headerCount := map[string]int{}
+ for i, csvColumnHeader := range headers {
+ curHeaderCount := headerCount[csvColumnHeader]
+ if fieldInfo := getCSVFieldPosition(csvColumnHeader, outInnerStructInfo, curHeaderCount); fieldInfo != nil {
+ csvHeadersLabels[i] = fieldInfo
+ if ShouldAlignDuplicateHeadersWithStructFieldOrder {
+ curHeaderCount++
+ headerCount[csvColumnHeader] = curHeaderCount
+ }
+ }
+ }
+ if err := maybeMissingStructFields(outInnerStructInfo.Fields, headers); err != nil {
+ if FailIfUnmatchedStructTags {
+ return err
+ }
+ }
+ if FailIfDoubleHeaderNames {
+ if err := maybeDoubleHeaderNames(headers); err != nil {
+ return err
+ }
+ }
+ i := 0
+ for {
+ line, err := decoder.getCSVRow()
+ if err == io.EOF {
+ break
+ } else if err != nil {
+ return err
+ }
+ outInner := createNewOutInner(outInnerWasPointer, outInnerType)
+ for j, csvColumnContent := range line {
+ if fieldInfo, ok := csvHeadersLabels[j]; ok { // Position found accordingly to header name
+ if err := setInnerField(&outInner, outInnerWasPointer, fieldInfo.IndexChain, csvColumnContent, fieldInfo.omitEmpty); err != nil { // Set field of struct
+ return &csv.ParseError{
+ Line: i + 2, //add 2 to account for the header & 0-indexing of arrays
+ Column: j + 1,
+ Err: err,
+ }
+ }
+ }
+ }
+ outValue.Send(outInner)
+ i++
+ }
+ return nil
+}
+
+// Check if the outType is an array or a slice
+func ensureOutType(outType reflect.Type) error {
+ switch outType.Kind() {
+ case reflect.Slice:
+ fallthrough
+ case reflect.Chan:
+ fallthrough
+ case reflect.Array:
+ return nil
+ }
+ return fmt.Errorf("cannot use " + outType.String() + ", only slice or array supported")
+}
+
+// Check if the outInnerType is of type struct
+func ensureOutInnerType(outInnerType reflect.Type) error {
+ switch outInnerType.Kind() {
+ case reflect.Struct:
+ return nil
+ }
+ return fmt.Errorf("cannot use " + outInnerType.String() + ", only struct supported")
+}
+
+func ensureOutCapacity(out *reflect.Value, csvLen int) error {
+ switch out.Kind() {
+ case reflect.Array:
+ if out.Len() < csvLen-1 { // Array is not big enough to hold the CSV content (arrays are not addressable)
+ return fmt.Errorf("array capacity problem: cannot store %d %s in %s", csvLen-1, out.Type().Elem().String(), out.Type().String())
+ }
+ case reflect.Slice:
+ if !out.CanAddr() && out.Len() < csvLen-1 { // Slice is not big enough tho hold the CSV content and is not addressable
+ return fmt.Errorf("slice capacity problem and is not addressable (did you forget &?)")
+ } else if out.CanAddr() && out.Len() < csvLen-1 {
+ out.Set(reflect.MakeSlice(out.Type(), csvLen-1, csvLen-1)) // Slice is not big enough, so grows it
+ }
+ }
+ return nil
+}
+
+func getCSVFieldPosition(key string, structInfo *structInfo, curHeaderCount int) *fieldInfo {
+ matchedFieldCount := 0
+ for _, field := range structInfo.Fields {
+ if field.matchesKey(key) {
+ if matchedFieldCount >= curHeaderCount {
+ return &field
+ } else {
+ matchedFieldCount++
+ }
+ }
+ }
+ return nil
+}
+
+func createNewOutInner(outInnerWasPointer bool, outInnerType reflect.Type) reflect.Value {
+ if outInnerWasPointer {
+ return reflect.New(outInnerType)
+ }
+ return reflect.New(outInnerType).Elem()
+}
+
+func setInnerField(outInner *reflect.Value, outInnerWasPointer bool, index []int, value string, omitEmpty bool) error {
+ oi := *outInner
+ if outInnerWasPointer {
+ oi = outInner.Elem()
+ }
+ return setField(oi.FieldByIndex(index), value, omitEmpty)
+}
diff --git a/vendor/github.com/gocarina/gocsv/encode.go b/vendor/github.com/gocarina/gocsv/encode.go
new file mode 100644
index 0000000..7a41f9f
--- /dev/null
+++ b/vendor/github.com/gocarina/gocsv/encode.go
@@ -0,0 +1,139 @@
+package gocsv
+
+import (
+ "fmt"
+ "io"
+ "reflect"
+)
+
+type encoder struct {
+ out io.Writer
+}
+
+func newEncoder(out io.Writer) *encoder {
+ return &encoder{out}
+}
+
+func writeFromChan(writer *SafeCSVWriter, c <-chan interface{}) error {
+ // Get the first value. It wil determine the header structure.
+ 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
+ }
+ inInnerWasPointer := inType.Kind() == reflect.Ptr
+ inInnerStructInfo := getStructInfo(inType) // Get the inner struct info to get CSV annotations
+ csvHeadersLabels := make([]string, len(inInnerStructInfo.Fields))
+ for i, fieldInfo := range inInnerStructInfo.Fields { // Used to write the header (first line) in CSV
+ csvHeadersLabels[i] = fieldInfo.getFirstKey()
+ }
+ if err := writer.Write(csvHeadersLabels); err != nil {
+ return err
+ }
+ write := func(val reflect.Value) error {
+ for j, fieldInfo := range inInnerStructInfo.Fields {
+ csvHeadersLabels[j] = ""
+ inInnerFieldValue, err := getInnerField(val, inInnerWasPointer, fieldInfo.IndexChain) // Get the correct field header <-> position
+ if err != nil {
+ return err
+ }
+ csvHeadersLabels[j] = inInnerFieldValue
+ }
+ if err := writer.Write(csvHeadersLabels); err != nil {
+ return err
+ }
+ return nil
+ }
+ if err := write(inValue); err != nil {
+ return err
+ }
+ for v := range c {
+ val, _ := getConcreteReflectValueAndType(v) // Get the concrete type (not pointer) (Slice<?> or Array<?>)
+ if err := ensureStructOrPtr(inType); err != nil {
+ return err
+ }
+ if err := write(val); err != nil {
+ return err
+ }
+ }
+ writer.Flush()
+ return writer.Error()
+}
+
+func writeTo(writer *SafeCSVWriter, in interface{}, omitHeaders bool) error {
+ inValue, inType := getConcreteReflectValueAndType(in) // Get the concrete type (not pointer) (Slice<?> or Array<?>)
+ if err := ensureInType(inType); err != nil {
+ return err
+ }
+ inInnerWasPointer, inInnerType := getConcreteContainerInnerType(inType) // Get the concrete inner type (not pointer) (Container<"?">)
+ if err := ensureInInnerType(inInnerType); err != nil {
+ return err
+ }
+ inInnerStructInfo := getStructInfo(inInnerType) // Get the inner struct info to get CSV annotations
+ csvHeadersLabels := make([]string, len(inInnerStructInfo.Fields))
+ for i, fieldInfo := range inInnerStructInfo.Fields { // Used to write the header (first line) in CSV
+ csvHeadersLabels[i] = fieldInfo.getFirstKey()
+ }
+ if !omitHeaders {
+ if err := writer.Write(csvHeadersLabels); err != nil {
+ return err
+ }
+ }
+ inLen := inValue.Len()
+ for i := 0; i < inLen; i++ { // Iterate over container rows
+ for j, fieldInfo := range inInnerStructInfo.Fields {
+ csvHeadersLabels[j] = ""
+ inInnerFieldValue, err := getInnerField(inValue.Index(i), inInnerWasPointer, fieldInfo.IndexChain) // Get the correct field header <-> position
+ if err != nil {
+ return err
+ }
+ csvHeadersLabels[j] = inInnerFieldValue
+ }
+ if err := writer.Write(csvHeadersLabels); err != nil {
+ return err
+ }
+ }
+ writer.Flush()
+ return writer.Error()
+}
+
+func ensureStructOrPtr(t reflect.Type) error {
+ switch t.Kind() {
+ case reflect.Struct:
+ fallthrough
+ case reflect.Ptr:
+ return nil
+ }
+ return fmt.Errorf("cannot use " + t.String() + ", only slice or array supported")
+}
+
+// Check if the inType is an array or a slice
+func ensureInType(outType reflect.Type) error {
+ switch outType.Kind() {
+ case reflect.Slice:
+ fallthrough
+ case reflect.Array:
+ return nil
+ }
+ return fmt.Errorf("cannot use " + outType.String() + ", only slice or array supported")
+}
+
+// Check if the inInnerType is of type struct
+func ensureInInnerType(outInnerType reflect.Type) error {
+ switch outInnerType.Kind() {
+ case reflect.Struct:
+ return nil
+ }
+ return fmt.Errorf("cannot use " + outInnerType.String() + ", only struct supported")
+}
+
+func getInnerField(outInner reflect.Value, outInnerWasPointer bool, index []int) (string, error) {
+ oi := outInner
+ if outInnerWasPointer {
+ oi = outInner.Elem()
+ }
+ return getFieldAsString(oi.FieldByIndex(index))
+}
diff --git a/vendor/github.com/gocarina/gocsv/reflect.go b/vendor/github.com/gocarina/gocsv/reflect.go
new file mode 100644
index 0000000..9217e30
--- /dev/null
+++ b/vendor/github.com/gocarina/gocsv/reflect.go
@@ -0,0 +1,107 @@
+package gocsv
+
+import (
+ "reflect"
+ "strings"
+ "sync"
+)
+
+// --------------------------------------------------------------------------
+// Reflection helpers
+
+type structInfo struct {
+ Fields []fieldInfo
+}
+
+// fieldInfo is a struct field that should be mapped to a CSV column, or vice-versa
+// Each IndexChain element before the last is the index of an the embedded struct field
+// that defines Key as a tag
+type fieldInfo struct {
+ keys []string
+ omitEmpty bool
+ IndexChain []int
+}
+
+func (f fieldInfo) getFirstKey() string {
+ return f.keys[0]
+}
+
+func (f fieldInfo) matchesKey(key string) bool {
+ for _, k := range f.keys {
+ if key == k || strings.TrimSpace(key) == k {
+ return true
+ }
+ }
+ return false
+}
+
+var structMap = make(map[reflect.Type]*structInfo)
+var structMapMutex sync.RWMutex
+
+func getStructInfo(rType reflect.Type) *structInfo {
+ structMapMutex.RLock()
+ stInfo, ok := structMap[rType]
+ structMapMutex.RUnlock()
+ if ok {
+ return stInfo
+ }
+ fieldsList := getFieldInfos(rType, []int{})
+ stInfo = &structInfo{fieldsList}
+ return stInfo
+}
+
+func getFieldInfos(rType reflect.Type, parentIndexChain []int) []fieldInfo {
+ fieldsCount := rType.NumField()
+ fieldsList := make([]fieldInfo, 0, fieldsCount)
+ for i := 0; i < fieldsCount; i++ {
+ field := rType.Field(i)
+ if field.PkgPath != "" {
+ continue
+ }
+ indexChain := append(parentIndexChain, i)
+ // if the field is an embedded struct, create a fieldInfo for each of its fields
+ if field.Anonymous && field.Type.Kind() == reflect.Struct {
+ fieldsList = append(fieldsList, getFieldInfos(field.Type, indexChain)...)
+ continue
+ }
+ fieldInfo := fieldInfo{IndexChain: indexChain}
+ fieldTag := field.Tag.Get("csv")
+ fieldTags := strings.Split(fieldTag, TagSeparator)
+ filteredTags := []string{}
+ for _, fieldTagEntry := range fieldTags {
+ if fieldTagEntry != "omitempty" {
+ filteredTags = append(filteredTags, fieldTagEntry)
+ } else {
+ fieldInfo.omitEmpty = true
+ }
+ }
+
+ if len(filteredTags) == 1 && filteredTags[0] == "-" {
+ continue
+ } else if len(filteredTags) > 0 && filteredTags[0] != "" {
+ fieldInfo.keys = filteredTags
+ } else {
+ fieldInfo.keys = []string{field.Name}
+ }
+ fieldsList = append(fieldsList, fieldInfo)
+ }
+ return fieldsList
+}
+
+func getConcreteContainerInnerType(in reflect.Type) (inInnerWasPointer bool, inInnerType reflect.Type) {
+ inInnerType = in.Elem()
+ inInnerWasPointer = false
+ if inInnerType.Kind() == reflect.Ptr {
+ inInnerWasPointer = true
+ inInnerType = inInnerType.Elem()
+ }
+ return inInnerWasPointer, inInnerType
+}
+
+func getConcreteReflectValueAndType(in interface{}) (reflect.Value, reflect.Type) {
+ value := reflect.ValueOf(in)
+ if value.Kind() == reflect.Ptr {
+ value = value.Elem()
+ }
+ return value, value.Type()
+}
diff --git a/vendor/github.com/gocarina/gocsv/safe_csv.go b/vendor/github.com/gocarina/gocsv/safe_csv.go
new file mode 100644
index 0000000..4b2882f
--- /dev/null
+++ b/vendor/github.com/gocarina/gocsv/safe_csv.go
@@ -0,0 +1,32 @@
+package gocsv
+
+//Wraps around SafeCSVWriter and makes it thread safe.
+import (
+ "encoding/csv"
+ "sync"
+)
+
+type SafeCSVWriter struct {
+ *csv.Writer
+ m sync.Mutex
+}
+
+func NewSafeCSVWriter(original *csv.Writer) *SafeCSVWriter {
+ return &SafeCSVWriter{
+ Writer: original,
+ }
+}
+
+//Override write
+func (w *SafeCSVWriter) Write(row []string) error {
+ w.m.Lock()
+ defer w.m.Unlock()
+ return w.Writer.Write(row)
+}
+
+//Override flush
+func (w *SafeCSVWriter) Flush() {
+ w.m.Lock()
+ w.Writer.Flush()
+ w.m.Unlock()
+}
diff --git a/vendor/github.com/gocarina/gocsv/types.go b/vendor/github.com/gocarina/gocsv/types.go
new file mode 100644
index 0000000..50d88ce
--- /dev/null
+++ b/vendor/github.com/gocarina/gocsv/types.go
@@ -0,0 +1,456 @@
+package gocsv
+
+import (
+ "encoding"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+// --------------------------------------------------------------------------
+// Conversion interfaces
+
+// TypeMarshaller is implemented by any value that has a MarshalCSV method
+// This converter is used to convert the value to it string representation
+type TypeMarshaller interface {
+ MarshalCSV() (string, error)
+}
+
+// Stringer is implemented by any value that has a String method
+// This converter is used to convert the value to it string representation
+// This converter will be used if your value does not implement TypeMarshaller
+type Stringer interface {
+ String() string
+}
+
+// TypeUnmarshaller is implemented by any value that has an UnmarshalCSV method
+// This converter is used to convert a string to your value representation of that string
+type TypeUnmarshaller interface {
+ UnmarshalCSV(string) error
+}
+
+// NoUnmarshalFuncError is the custom error type to be raised in case there is no unmarshal function defined on type
+type NoUnmarshalFuncError struct {
+ msg string
+}
+
+func (e NoUnmarshalFuncError) Error() string {
+ return e.msg
+}
+
+// NoMarshalFuncError is the custom error type to be raised in case there is no marshal function defined on type
+type NoMarshalFuncError struct {
+ msg string
+}
+
+func (e NoMarshalFuncError) Error() string {
+ return e.msg
+}
+
+var (
+ stringerType = reflect.TypeOf((*Stringer)(nil)).Elem()
+ marshallerType = reflect.TypeOf((*TypeMarshaller)(nil)).Elem()
+ unMarshallerType = reflect.TypeOf((*TypeUnmarshaller)(nil)).Elem()
+ textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
+ textUnMarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
+)
+
+// --------------------------------------------------------------------------
+// Conversion helpers
+
+func toString(in interface{}) (string, error) {
+ inValue := reflect.ValueOf(in)
+
+ switch inValue.Kind() {
+ case reflect.String:
+ return inValue.String(), nil
+ case reflect.Bool:
+ b := inValue.Bool()
+ if b {
+ return "true", nil
+ }
+ return "false", nil
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return fmt.Sprintf("%v", inValue.Int()), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return fmt.Sprintf("%v", inValue.Uint()), nil
+ case reflect.Float32:
+ return strconv.FormatFloat(inValue.Float(), byte('f'), -1, 32), nil
+ case reflect.Float64:
+ return strconv.FormatFloat(inValue.Float(), byte('f'), -1, 64), nil
+ }
+ return "", fmt.Errorf("No known conversion from " + inValue.Type().String() + " to string")
+}
+
+func toBool(in interface{}) (bool, error) {
+ inValue := reflect.ValueOf(in)
+
+ switch inValue.Kind() {
+ case reflect.String:
+ s := inValue.String()
+ switch s {
+ case "yes":
+ return true, nil
+ case "no", "":
+ return false, nil
+ default:
+ return strconv.ParseBool(s)
+ }
+ case reflect.Bool:
+ return inValue.Bool(), nil
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ i := inValue.Int()
+ if i != 0 {
+ return true, nil
+ }
+ return false, nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ i := inValue.Uint()
+ if i != 0 {
+ return true, nil
+ }
+ return false, nil
+ case reflect.Float32, reflect.Float64:
+ f := inValue.Float()
+ if f != 0 {
+ return true, nil
+ }
+ return false, nil
+ }
+ return false, fmt.Errorf("No known conversion from " + inValue.Type().String() + " to bool")
+}
+
+func toInt(in interface{}) (int64, error) {
+ inValue := reflect.ValueOf(in)
+
+ switch inValue.Kind() {
+ case reflect.String:
+ s := strings.TrimSpace(inValue.String())
+ if s == "" {
+ return 0, nil
+ }
+ return strconv.ParseInt(s, 0, 64)
+ case reflect.Bool:
+ if inValue.Bool() {
+ return 1, nil
+ }
+ return 0, nil
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return inValue.Int(), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return int64(inValue.Uint()), nil
+ case reflect.Float32, reflect.Float64:
+ return int64(inValue.Float()), nil
+ }
+ return 0, fmt.Errorf("No known conversion from " + inValue.Type().String() + " to int")
+}
+
+func toUint(in interface{}) (uint64, error) {
+ inValue := reflect.ValueOf(in)
+
+ switch inValue.Kind() {
+ case reflect.String:
+ s := strings.TrimSpace(inValue.String())
+ if s == "" {
+ return 0, nil
+ }
+
+ // support the float input
+ if strings.Contains(s, ".") {
+ f, err := strconv.ParseFloat(s, 64)
+ if err != nil {
+ return 0, err
+ }
+ return uint64(f), nil
+ }
+ return strconv.ParseUint(s, 0, 64)
+ case reflect.Bool:
+ if inValue.Bool() {
+ return 1, nil
+ }
+ return 0, nil
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return uint64(inValue.Int()), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return inValue.Uint(), nil
+ case reflect.Float32, reflect.Float64:
+ return uint64(inValue.Float()), nil
+ }
+ return 0, fmt.Errorf("No known conversion from " + inValue.Type().String() + " to uint")
+}
+
+func toFloat(in interface{}) (float64, error) {
+ inValue := reflect.ValueOf(in)
+
+ switch inValue.Kind() {
+ case reflect.String:
+ s := strings.TrimSpace(inValue.String())
+ if s == "" {
+ return 0, nil
+ }
+ return strconv.ParseFloat(s, 64)
+ case reflect.Bool:
+ if inValue.Bool() {
+ return 1, nil
+ }
+ return 0, nil
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return float64(inValue.Int()), nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return float64(inValue.Uint()), nil
+ case reflect.Float32, reflect.Float64:
+ return inValue.Float(), nil
+ }
+ return 0, fmt.Errorf("No known conversion from " + inValue.Type().String() + " to float")
+}
+
+func setField(field reflect.Value, value string, omitEmpty bool) error {
+ if field.Kind() == reflect.Ptr {
+ if omitEmpty && value == "" {
+ return nil
+ }
+ if field.IsNil() {
+ field.Set(reflect.New(field.Type().Elem()))
+ }
+ field = field.Elem()
+ }
+
+ switch field.Interface().(type) {
+ case string:
+ s, err := toString(value)
+ if err != nil {
+ return err
+ }
+ field.SetString(s)
+ case bool:
+ b, err := toBool(value)
+ if err != nil {
+ return err
+ }
+ field.SetBool(b)
+ case int, int8, int16, int32, int64:
+ i, err := toInt(value)
+ if err != nil {
+ return err
+ }
+ field.SetInt(i)
+ case uint, uint8, uint16, uint32, uint64:
+ ui, err := toUint(value)
+ if err != nil {
+ return err
+ }
+ field.SetUint(ui)
+ case float32, float64:
+ f, err := toFloat(value)
+ if err != nil {
+ return err
+ }
+ field.SetFloat(f)
+ default:
+ // Not a native type, check for unmarshal method
+ if err := unmarshall(field, value); err != nil {
+ if _, ok := err.(NoUnmarshalFuncError); !ok {
+ return err
+ }
+ // Could not unmarshal, check for kind, e.g. renamed type from basic type
+ switch field.Kind() {
+ case reflect.String:
+ s, err := toString(value)
+ if err != nil {
+ return err
+ }
+ field.SetString(s)
+ case reflect.Bool:
+ b, err := toBool(value)
+ if err != nil {
+ return err
+ }
+ field.SetBool(b)
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ i, err := toInt(value)
+ if err != nil {
+ return err
+ }
+ field.SetInt(i)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ ui, err := toUint(value)
+ if err != nil {
+ return err
+ }
+ field.SetUint(ui)
+ case reflect.Float32, reflect.Float64:
+ f, err := toFloat(value)
+ if err != nil {
+ return err
+ }
+ field.SetFloat(f)
+ default:
+ return err
+ }
+ } else {
+ return nil
+ }
+ }
+ return nil
+}
+
+func getFieldAsString(field reflect.Value) (str string, err error) {
+ switch field.Kind() {
+ case reflect.Interface:
+ case reflect.Ptr:
+ if field.IsNil() {
+ return "", nil
+ }
+ return getFieldAsString(field.Elem())
+ default:
+ // Check if field is go native type
+ switch field.Interface().(type) {
+ case string:
+ return field.String(), nil
+ case bool:
+ str, err = toString(field.Bool())
+ if err != nil {
+ return str, err
+ }
+ case int, int8, int16, int32, int64:
+ str, err = toString(field.Int())
+ if err != nil {
+ return str, err
+ }
+ case uint, uint8, uint16, uint32, uint64:
+ str, err = toString(field.Uint())
+ if err != nil {
+ return str, err
+ }
+ case float32:
+ str, err = toString(float32(field.Float()))
+ if err != nil {
+ return str, err
+ }
+ case float64:
+ str, err = toString(field.Float())
+ if err != nil {
+ return str, err
+ }
+ default:
+ // Not a native type, check for marshal method
+ str, err = marshall(field)
+ if err != nil {
+ if _, ok := err.(NoMarshalFuncError); !ok {
+ return str, err
+ }
+ // If not marshal method, is field compatible with/renamed from native type
+ switch field.Kind() {
+ case reflect.String:
+ return field.String(), nil
+ case reflect.Bool:
+ str, err = toString(field.Bool())
+ if err != nil {
+ return str, err
+ }
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ str, err = toString(field.Int())
+ if err != nil {
+ return str, err
+ }
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ str, err = toString(field.Uint())
+ if err != nil {
+ return str, err
+ }
+ case reflect.Float32:
+ str, err = toString(float32(field.Float()))
+ if err != nil {
+ return str, err
+ }
+ case reflect.Float64:
+ str, err = toString(field.Float())
+ if err != nil {
+ return str, err
+ }
+ }
+ } else {
+ return str, nil
+ }
+ }
+ }
+ return str, nil
+}
+
+// --------------------------------------------------------------------------
+// Un/serializations helpers
+
+func unmarshall(field reflect.Value, value string) error {
+ dupField := field
+ unMarshallIt := func(finalField reflect.Value) error {
+ if finalField.CanInterface() {
+ fieldIface := finalField.Interface()
+
+ fieldTypeUnmarshaller, ok := fieldIface.(TypeUnmarshaller)
+ if ok {
+ return fieldTypeUnmarshaller.UnmarshalCSV(value)
+ }
+
+ // Otherwise try to use TextUnmarshaler
+ fieldTextUnmarshaler, ok := fieldIface.(encoding.TextUnmarshaler)
+ if ok {
+ return fieldTextUnmarshaler.UnmarshalText([]byte(value))
+ }
+ }
+
+ return NoUnmarshalFuncError{"No known conversion from string to " + field.Type().String() + ", " + field.Type().String() + " does not implement TypeUnmarshaller"}
+ }
+ for dupField.Kind() == reflect.Interface || dupField.Kind() == reflect.Ptr {
+ if dupField.IsNil() {
+ dupField = reflect.New(field.Type().Elem())
+ field.Set(dupField)
+ return unMarshallIt(dupField)
+ }
+ dupField = dupField.Elem()
+ }
+ if dupField.CanAddr() {
+ return unMarshallIt(dupField.Addr())
+ }
+ return NoUnmarshalFuncError{"No known conversion from string to " + field.Type().String() + ", " + field.Type().String() + " does not implement TypeUnmarshaller"}
+}
+
+func marshall(field reflect.Value) (value string, err error) {
+ dupField := field
+ marshallIt := func(finalField reflect.Value) (string, error) {
+ if finalField.CanInterface() {
+ fieldIface := finalField.Interface()
+
+ // Use TypeMarshaller when possible
+ fieldTypeMarhaller, ok := fieldIface.(TypeMarshaller)
+ if ok {
+ return fieldTypeMarhaller.MarshalCSV()
+ }
+
+ // Otherwise try to use TextMarshaller
+ fieldTextMarshaler, ok := fieldIface.(encoding.TextMarshaler)
+ if ok {
+ text, err := fieldTextMarshaler.MarshalText()
+ return string(text), err
+ }
+
+ // Otherwise try to use Stringer
+ fieldStringer, ok := fieldIface.(Stringer)
+ if ok {
+ return fieldStringer.String(), nil
+ }
+ }
+
+ return value, NoMarshalFuncError{"No known conversion from " + field.Type().String() + " to string, " + field.Type().String() + " does not implement TypeMarshaller nor Stringer"}
+ }
+ for dupField.Kind() == reflect.Interface || dupField.Kind() == reflect.Ptr {
+ if dupField.IsNil() {
+ return value, nil
+ }
+ dupField = dupField.Elem()
+ }
+ if dupField.CanAddr() {
+ return marshallIt(dupField.Addr())
+ }
+ return value, NoMarshalFuncError{"No known conversion from " + field.Type().String() + " to string, " + field.Type().String() + " does not implement TypeMarshaller nor Stringer"}
+}
diff --git a/vendor/github.com/gocarina/gocsv/unmarshaller.go b/vendor/github.com/gocarina/gocsv/unmarshaller.go
new file mode 100644
index 0000000..ace601e
--- /dev/null
+++ b/vendor/github.com/gocarina/gocsv/unmarshaller.go
@@ -0,0 +1,97 @@
+package gocsv
+
+import (
+ "encoding/csv"
+ "errors"
+ "fmt"
+ "reflect"
+)
+
+// Unmarshaller is a CSV to struct unmarshaller.
+type Unmarshaller struct {
+ reader *csv.Reader
+ fieldInfoMap map[int]*fieldInfo
+ MismatchedHeaders []string
+ MismatchedStructFields []string
+ outType reflect.Type
+}
+
+// NewUnmarshaller creates an unmarshaller from a csv.Reader and a struct.
+func NewUnmarshaller(reader *csv.Reader, out interface{}) (*Unmarshaller, error) {
+ headers, err := reader.Read()
+ if err != nil {
+ return nil, err
+ }
+
+ um := &Unmarshaller{reader: reader, outType: reflect.TypeOf(out)}
+ err = validate(um, out, headers)
+ if err != nil {
+ return nil, err
+ }
+ return um, nil
+}
+
+// Read returns an interface{} whose runtime type is the same as the struct that
+// was used to create the Unmarshaller.
+func (um *Unmarshaller) Read() (interface{}, error) {
+ row, err := um.reader.Read()
+ if err != nil {
+ return nil, err
+ }
+ return um.unmarshalRow(row)
+}
+
+// validate ensures that a struct was used to create the Unmarshaller, and validates
+// CSV headers against the CSV tags in the struct.
+func validate(um *Unmarshaller, s interface{}, headers []string) error {
+ concreteType := reflect.TypeOf(s)
+ if concreteType.Kind() == reflect.Ptr {
+ concreteType = concreteType.Elem()
+ }
+ if err := ensureOutInnerType(concreteType); err != nil {
+ return err
+ }
+ structInfo := getStructInfo(concreteType) // Get struct info to get CSV annotations.
+ if len(structInfo.Fields) == 0 {
+ return errors.New("no csv struct tags found")
+ }
+ csvHeadersLabels := make(map[int]*fieldInfo, len(structInfo.Fields)) // Used to store the corresponding header <-> position in CSV
+ headerCount := map[string]int{}
+ for i, csvColumnHeader := range headers {
+ curHeaderCount := headerCount[csvColumnHeader]
+ if fieldInfo := getCSVFieldPosition(csvColumnHeader, structInfo, curHeaderCount); fieldInfo != nil {
+ csvHeadersLabels[i] = fieldInfo
+ if ShouldAlignDuplicateHeadersWithStructFieldOrder {
+ curHeaderCount++
+ headerCount[csvColumnHeader] = curHeaderCount
+ }
+ }
+ }
+ if err := maybeDoubleHeaderNames(headers); err != nil {
+ return err
+ }
+
+ um.fieldInfoMap = csvHeadersLabels
+ um.MismatchedHeaders = mismatchHeaderFields(structInfo.Fields, headers)
+ um.MismatchedStructFields = mismatchStructFields(structInfo.Fields, headers)
+ return nil
+}
+
+// unmarshalRow converts a CSV row to a struct, based on CSV struct tags.
+func (um *Unmarshaller) unmarshalRow(row []string) (interface{}, error) {
+ isPointer := false
+ concreteOutType := um.outType
+ if um.outType.Kind() == reflect.Ptr {
+ isPointer = true
+ concreteOutType = concreteOutType.Elem()
+ }
+ outValue := createNewOutInner(isPointer, concreteOutType)
+ for j, csvColumnContent := range row {
+ if fieldInfo, ok := um.fieldInfoMap[j]; ok {
+ if err := setInnerField(&outValue, isPointer, fieldInfo.IndexChain, csvColumnContent, fieldInfo.omitEmpty); err != nil { // Set field of struct
+ return nil, fmt.Errorf("cannot assign field at %v to %s through index chain %v: %v", j, outValue.Type(), fieldInfo.IndexChain, err)
+ }
+ }
+ }
+ return outValue.Interface(), nil
+}