Bläddra i källkod

Add gocarina csv

Andrea Fazzi 6 år sedan
förälder
incheckning
1b2df6f79a

+ 4 - 0
Godeps/Godeps.json

@@ -17,6 +17,10 @@
 			"Comment": "v1.3-49-gcd4cb90",
 			"Rev": "cd4cb909ce1a31435164be29bf3682031f61539a"
 		},
+		{
+			"ImportPath": "github.com/gocarina/gocsv",
+			"Rev": "7099e67763c29f812fa2ed7083f32e38be60125a"
+		},
 		{
 			"ImportPath": "github.com/gorilla/context",
 			"Comment": "v1.1-7-g08b5f42",

+ 1 - 0
vendor/github.com/gocarina/gocsv/.gitignore

@@ -0,0 +1 @@
+.idea

+ 1 - 0
vendor/github.com/gocarina/gocsv/.travis.yml

@@ -0,0 +1 @@
+language: go

+ 21 - 0
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.

+ 170 - 0
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)
+
+        ...
+}
+
+```

+ 319 - 0
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
+}

+ 296 - 0
vendor/github.com/gocarina/gocsv/decode.go

@@ -0,0 +1,296 @@
+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 maybeMissingStructFields(structInfo []fieldInfo, headers []string) error {
+	if len(structInfo) == 0 {
+		return nil
+	}
+
+	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 {
+			return fmt.Errorf("found unmatched struct field with tags %v", info.keys)
+		}
+	}
+	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)
+}

+ 139 - 0
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))
+}

+ 107 - 0
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()
+}

+ 32 - 0
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()
+}

+ 439 - 0
vendor/github.com/gocarina/gocsv/types.go

@@ -0,0 +1,439 @@
+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() && finalField.Type().Implements(unMarshallerType) {
+			if err := finalField.Interface().(TypeUnmarshaller).UnmarshalCSV(value); err != nil {
+				return err
+			}
+			return nil
+		} else if finalField.CanInterface() && finalField.Type().Implements(textUnMarshalerType) { // Otherwise try to use TextMarshaller
+			if err := finalField.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(value)); err != nil {
+				return err
+			}
+			return nil
+		}
+
+		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() && finalField.Type().Implements(marshallerType) { // Use TypeMarshaller when possible
+			return finalField.Interface().(TypeMarshaller).MarshalCSV()
+		} else if finalField.CanInterface() && finalField.Type().Implements(textMarshalerType) { // Otherwise try to use TextMarshaller
+			text, err := finalField.Interface().(encoding.TextMarshaler).MarshalText()
+			return string(text), err
+		} else if finalField.CanInterface() && finalField.Type().Implements(stringerType) { // Otherwise try to use Stringer
+			return finalField.Interface().(Stringer).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"}
+}