summaryrefslogtreecommitdiff
path: root/vendor/github.com/gocarina/gocsv/unmarshaller.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/gocarina/gocsv/unmarshaller.go')
-rw-r--r--vendor/github.com/gocarina/gocsv/unmarshaller.go35
1 files changed, 28 insertions, 7 deletions
diff --git a/vendor/github.com/gocarina/gocsv/unmarshaller.go b/vendor/github.com/gocarina/gocsv/unmarshaller.go
index ace601e..87e6a8f 100644
--- a/vendor/github.com/gocarina/gocsv/unmarshaller.go
+++ b/vendor/github.com/gocarina/gocsv/unmarshaller.go
@@ -10,7 +10,8 @@ import (
// Unmarshaller is a CSV to struct unmarshaller.
type Unmarshaller struct {
reader *csv.Reader
- fieldInfoMap map[int]*fieldInfo
+ Headers []string
+ fieldInfoMap []*fieldInfo
MismatchedHeaders []string
MismatchedStructFields []string
outType reflect.Type
@@ -22,6 +23,7 @@ func NewUnmarshaller(reader *csv.Reader, out interface{}) (*Unmarshaller, error)
if err != nil {
return nil, err
}
+ headers = normalizeHeaders(headers)
um := &Unmarshaller{reader: reader, outType: reflect.TypeOf(out)}
err = validate(um, out, headers)
@@ -38,7 +40,18 @@ func (um *Unmarshaller) Read() (interface{}, error) {
if err != nil {
return nil, err
}
- return um.unmarshalRow(row)
+ return um.unmarshalRow(row, nil)
+}
+
+// ReadUnmatched is same as Read(), but returns a map of the columns that didn't match a field in the struct
+func (um *Unmarshaller) ReadUnmatched() (interface{}, map[string]string, error) {
+ row, err := um.reader.Read()
+ if err != nil {
+ return nil, nil, err
+ }
+ unmatched := make(map[string]string)
+ value, err := um.unmarshalRow(row, unmatched)
+ return value, unmatched, err
}
// validate ensures that a struct was used to create the Unmarshaller, and validates
@@ -55,7 +68,7 @@ func validate(um *Unmarshaller, s interface{}, headers []string) error {
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
+ csvHeadersLabels := make([]*fieldInfo, len(headers)) // Used to store the corresponding header <-> position in CSV
headerCount := map[string]int{}
for i, csvColumnHeader := range headers {
curHeaderCount := headerCount[csvColumnHeader]
@@ -67,10 +80,14 @@ func validate(um *Unmarshaller, s interface{}, headers []string) error {
}
}
}
- if err := maybeDoubleHeaderNames(headers); err != nil {
- return err
+
+ if FailIfDoubleHeaderNames {
+ if err := maybeDoubleHeaderNames(headers); err != nil {
+ return err
+ }
}
+ um.Headers = headers
um.fieldInfoMap = csvHeadersLabels
um.MismatchedHeaders = mismatchHeaderFields(structInfo.Fields, headers)
um.MismatchedStructFields = mismatchStructFields(structInfo.Fields, headers)
@@ -78,7 +95,8 @@ func validate(um *Unmarshaller, s interface{}, headers []string) error {
}
// unmarshalRow converts a CSV row to a struct, based on CSV struct tags.
-func (um *Unmarshaller) unmarshalRow(row []string) (interface{}, error) {
+// If unmatched is non nil, it is populated with any columns that don't map to a struct field
+func (um *Unmarshaller) unmarshalRow(row []string, unmatched map[string]string) (interface{}, error) {
isPointer := false
concreteOutType := um.outType
if um.outType.Kind() == reflect.Ptr {
@@ -87,10 +105,13 @@ func (um *Unmarshaller) unmarshalRow(row []string) (interface{}, error) {
}
outValue := createNewOutInner(isPointer, concreteOutType)
for j, csvColumnContent := range row {
- if fieldInfo, ok := um.fieldInfoMap[j]; ok {
+ if j < len(um.fieldInfoMap) && um.fieldInfoMap[j] != nil {
+ fieldInfo := um.fieldInfoMap[j]
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)
}
+ } else if unmatched != nil {
+ unmatched[um.Headers[j]] = csvColumnContent
}
}
return outValue.Interface(), nil