From 1bbce1aa65e4af84a144b137ea2037004eb2881d Mon Sep 17 00:00:00 2001 From: Victor Häggqvist Date: Wed, 14 Sep 2022 23:54:17 +0200 Subject: go bump --- vendor/github.com/gocarina/gocsv/.travis.yml | 3 + vendor/github.com/gocarina/gocsv/README.md | 2 +- vendor/github.com/gocarina/gocsv/csv.go | 76 +++++++++++++++++-- vendor/github.com/gocarina/gocsv/decode.go | 69 ++++++++++++----- vendor/github.com/gocarina/gocsv/encode.go | 32 ++++++-- vendor/github.com/gocarina/gocsv/go.mod | 3 - vendor/github.com/gocarina/gocsv/reflect.go | 95 ++++++++++++++++++++---- vendor/github.com/gocarina/gocsv/safe_csv.go | 6 ++ vendor/github.com/gocarina/gocsv/types.go | 22 +++++- vendor/github.com/gocarina/gocsv/unmarshaller.go | 20 ++++- vendor/github.com/spf13/pflag/go.mod | 3 - vendor/github.com/spf13/pflag/go.sum | 0 12 files changed, 273 insertions(+), 58 deletions(-) delete mode 100644 vendor/github.com/gocarina/gocsv/go.mod delete mode 100644 vendor/github.com/spf13/pflag/go.mod delete mode 100644 vendor/github.com/spf13/pflag/go.sum (limited to 'vendor/github.com') diff --git a/vendor/github.com/gocarina/gocsv/.travis.yml b/vendor/github.com/gocarina/gocsv/.travis.yml index 4f2ee4d..61c24c6 100644 --- a/vendor/github.com/gocarina/gocsv/.travis.yml +++ b/vendor/github.com/gocarina/gocsv/.travis.yml @@ -1 +1,4 @@ language: go +arch: + - amd64 + - ppc64le diff --git a/vendor/github.com/gocarina/gocsv/README.md b/vendor/github.com/gocarina/gocsv/README.md index e606ac7..6f0d344 100644 --- a/vendor/github.com/gocarina/gocsv/README.md +++ b/vendor/github.com/gocarina/gocsv/README.md @@ -152,7 +152,7 @@ func main() { ... - gocsv.SetCSVWriter(func(out io.Writer) *SafeCSVWriter { + gocsv.SetCSVWriter(func(out io.Writer) *gocsv.SafeCSVWriter { writer := csv.NewWriter(out) writer.Comma = '|' return gocsv.NewSafeCSVWriter(writer) diff --git a/vendor/github.com/gocarina/gocsv/csv.go b/vendor/github.com/gocarina/gocsv/csv.go index 22846c0..3ba3efb 100644 --- a/vendor/github.com/gocarina/gocsv/csv.go +++ b/vendor/github.com/gocarina/gocsv/csv.go @@ -127,6 +127,15 @@ func MarshalString(in interface{}) (out string, err error) { return bufferString.String(), nil } +// MarshalStringWithoutHeaders returns the CSV string from the interface. +func MarshalStringWithoutHeaders(in interface{}) (out string, err error) { + bufferString := bytes.NewBufferString(out) + if err := MarshalWithoutHeaders(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) @@ -149,17 +158,22 @@ func MarshalWithoutHeaders(in interface{}, out io.Writer) (err error) { } // MarshalChan returns the CSV read from the channel. -func MarshalChan(c <-chan interface{}, out *SafeCSVWriter) error { - return writeFromChan(out, c) +func MarshalChan(c <-chan interface{}, out CSVWriter) error { + return writeFromChan(out, c, false) +} + +// MarshalChanWithoutHeaders returns the CSV read from the channel. +func MarshalChanWithoutHeaders(c <-chan interface{}, out CSVWriter) error { + return writeFromChan(out, c, true) } // MarshalCSV returns the CSV in writer from the interface. -func MarshalCSV(in interface{}, out *SafeCSVWriter) (err error) { +func MarshalCSV(in interface{}, out CSVWriter) (err error) { return writeTo(out, in, false) } // MarshalCSVWithoutHeaders returns the CSV in writer from the interface. -func MarshalCSVWithoutHeaders(in interface{}, out *SafeCSVWriter) (err error) { +func MarshalCSVWithoutHeaders(in interface{}, out CSVWriter) (err error) { return writeTo(out, in, true) } @@ -216,6 +230,43 @@ func UnmarshalCSV(in CSVReader, out interface{}) error { return readTo(csvDecoder{in}, out) } +// UnmarshalCSVToMap parses a CSV of 2 columns into a map. +func UnmarshalCSVToMap(in CSVReader, out interface{}) error { + decoder := NewSimpleDecoderFromCSVReader(in) + header, err := decoder.GetCSVRow() + if err != nil { + return err + } + if len(header) != 2 { + return fmt.Errorf("maps can only be created for csv of two columns") + } + outValue, outType := getConcreteReflectValueAndType(out) + if outType.Kind() != reflect.Map { + return fmt.Errorf("cannot use " + outType.String() + ", only map supported") + } + keyType := outType.Key() + valueType := outType.Elem() + outValue.Set(reflect.MakeMap(outType)) + for { + key := reflect.New(keyType) + value := reflect.New(valueType) + line, err := decoder.GetCSVRow() + if err == io.EOF { + break + } else if err != nil { + return err + } + if err := setField(key, line[0], false); err != nil { + return err + } + if err := setField(value, line[1], false); err != nil { + return err + } + outValue.SetMapIndex(key.Elem(), value.Elem()) + } + return nil +} + // 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 { @@ -278,7 +329,13 @@ func UnmarshalToCallback(in io.Reader, f interface{}) error { if !notClosed || v.Interface() == nil { break } - valueFunc.Call([]reflect.Value{v}) + callResults := valueFunc.Call([]reflect.Value{v}) + // if last returned value from Call() is an error, return it + if len(callResults) > 0 { + if err, ok := callResults[len(callResults)-1].Interface().(error); ok { + return err + } + } } return nil } @@ -340,7 +397,7 @@ func UnmarshalToCallbackWithError(in io.Reader, f interface{}) error { return fmt.Errorf("the given function must have exactly one return value") } if !isErrorType(t.Out(0)) { - return fmt.Errorf("the given function must only return error.") + return fmt.Errorf("the given function must only return error") } cerr := make(chan error) @@ -361,6 +418,9 @@ func UnmarshalToCallbackWithError(in io.Reader, f interface{}) error { } v, notClosed := c.Recv() if !notClosed || v.Interface() == nil { + if err := <-cerr; err != nil { + fErr = err + } break } @@ -405,7 +465,7 @@ func UnmarshalStringToCallbackWithError(in string, c interface{}) (err error) { // CSVToMap creates a simple map from a CSV of 2 columns. func CSVToMap(in io.Reader) (map[string]string, error) { decoder := newSimpleDecoderFromReader(in) - header, err := decoder.getCSVRow() + header, err := decoder.GetCSVRow() if err != nil { return nil, err } @@ -414,7 +474,7 @@ func CSVToMap(in io.Reader) (map[string]string, error) { } m := make(map[string]string) for { - line, err := decoder.getCSVRow() + line, err := decoder.GetCSVRow() if err == io.EOF { break } else if err != nil { diff --git a/vendor/github.com/gocarina/gocsv/decode.go b/vendor/github.com/gocarina/gocsv/decode.go index a832e84..537251d 100644 --- a/vendor/github.com/gocarina/gocsv/decode.go +++ b/vendor/github.com/gocarina/gocsv/decode.go @@ -10,13 +10,13 @@ import ( // Decoder . type Decoder interface { - getCSVRows() ([][]string, error) + GetCSVRows() ([][]string, error) } // SimpleDecoder . type SimpleDecoder interface { - getCSVRow() ([]string, error) - getCSVRows() ([][]string, error) + GetCSVRow() ([]string, error) + GetCSVRows() ([][]string, error) } type CSVReader interface { @@ -32,6 +32,11 @@ func newSimpleDecoderFromReader(r io.Reader) SimpleDecoder { return csvDecoder{getCSVReader(r)} } +var ( + ErrEmptyCSVFile = errors.New("empty csv file given") + ErrNoStructTags = errors.New("no csv struct tags found") +) + // NewSimpleDecoderFromCSVReader creates a SimpleDecoder, which may be passed // to the UnmarshalDecoder* family of functions, from a CSV reader. Note that // encoding/csv.Reader implements CSVReader, so you can pass one of those @@ -40,11 +45,11 @@ func NewSimpleDecoderFromCSVReader(r CSVReader) SimpleDecoder { return csvDecoder{r} } -func (c csvDecoder) getCSVRows() ([][]string, error) { +func (c csvDecoder) GetCSVRows() ([][]string, error) { return c.ReadAll() } -func (c csvDecoder) getCSVRow() ([]string, error) { +func (c csvDecoder) GetCSVRow() ([]string, error) { return c.Read() } @@ -137,19 +142,19 @@ func readToWithErrorHandler(decoder Decoder, errHandler ErrorHandler, out interf if err := ensureOutInnerType(outInnerType); err != nil { return err } - csvRows, err := decoder.getCSVRows() // Get the CSV csvRows + csvRows, err := decoder.GetCSVRows() // Get the CSV csvRows if err != nil { return err } if len(csvRows) == 0 { - return errors.New("empty csv file given") + return ErrEmptyCSVFile } 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") + return ErrNoStructTags } headers := normalizeHeaders(csvRows[0]) @@ -203,8 +208,11 @@ func readToWithErrorHandler(decoder Decoder, errHandler ErrorHandler, out interf continue } } - - if err := setInnerField(&outInner, outInnerWasPointer, fieldInfo.IndexChain, csvColumnContent, fieldInfo.omitEmpty); err != nil { // Set field of struct + value := csvColumnContent + if value == "" { + value = fieldInfo.defaultValue + } + if err := setInnerField(&outInner, outInnerWasPointer, fieldInfo.IndexChain, value, fieldInfo.omitEmpty); err != nil { // Set field of struct parseError := csv.ParseError{ Line: i + 2, //add 2 to account for the header & 0-indexing of arrays Column: j + 1, @@ -234,7 +242,7 @@ func readEach(decoder SimpleDecoder, c interface{}) error { } defer outValue.Close() - headers, err := decoder.getCSVRow() + headers, err := decoder.GetCSVRow() if err != nil { return err } @@ -246,7 +254,7 @@ func readEach(decoder SimpleDecoder, c interface{}) error { } 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") + return ErrNoStructTags } csvHeadersLabels := make(map[int]*fieldInfo, len(outInnerStructInfo.Fields)) // Used to store the correspondance header <-> position in CSV headerCount := map[string]int{} @@ -272,7 +280,7 @@ func readEach(decoder SimpleDecoder, c interface{}) error { } i := 0 for { - line, err := decoder.getCSVRow() + line, err := decoder.GetCSVRow() if err == io.EOF { break } else if err != nil { @@ -309,12 +317,12 @@ func readEachWithoutHeaders(decoder SimpleDecoder, c interface{}) error { } 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") + return ErrNoStructTags } i := 0 for { - line, err := decoder.getCSVRow() + line, err := decoder.GetCSVRow() if err == io.EOF { break } else if err != nil { @@ -346,19 +354,19 @@ func readToWithoutHeaders(decoder Decoder, out interface{}) error { if err := ensureOutInnerType(outInnerType); err != nil { return err } - csvRows, err := decoder.getCSVRows() // Get the CSV csvRows + csvRows, err := decoder.GetCSVRows() // Get the CSV csvRows if err != nil { return err } if len(csvRows) == 0 { - return errors.New("empty csv file given") + return ErrEmptyCSVFile } if err := ensureOutCapacity(&outValue, len(csvRows)+1); 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") + return ErrNoStructTags } for i, csvRow := range csvRows { @@ -446,6 +454,31 @@ func setInnerField(outInner *reflect.Value, outInnerWasPointer bool, index []int } oi = outInner.Elem() } + + if oi.Kind() == reflect.Slice || oi.Kind() == reflect.Array { + i := index[0] + + // grow slice when needed + if i >= oi.Cap() { + newcap := oi.Cap() + oi.Cap()/2 + if newcap < 4 { + newcap = 4 + } + newoi := reflect.MakeSlice(oi.Type(), oi.Len(), newcap) + reflect.Copy(newoi, oi) + oi.Set(newoi) + } + if i >= oi.Len() { + oi.SetLen(i + 1) + } + + item := oi.Index(i) + if len(index) > 1 { + return setInnerField(&item, false, index[1:], value, omitEmpty) + } + return setField(item, value, omitEmpty) + } + // because pointers can be nil need to recurse one index at a time and perform nil check if len(index) > 1 { nextField := oi.Field(index[0]) diff --git a/vendor/github.com/gocarina/gocsv/encode.go b/vendor/github.com/gocarina/gocsv/encode.go index 8671533..a7c0e72 100644 --- a/vendor/github.com/gocarina/gocsv/encode.go +++ b/vendor/github.com/gocarina/gocsv/encode.go @@ -1,11 +1,16 @@ package gocsv import ( + "errors" "fmt" "io" "reflect" ) +var ( + ErrChannelIsClosed = errors.New("channel is closed") +) + type encoder struct { out io.Writer } @@ -14,11 +19,11 @@ func newEncoder(out io.Writer) *encoder { return &encoder{out} } -func writeFromChan(writer *SafeCSVWriter, c <-chan interface{}) error { +func writeFromChan(writer CSVWriter, c <-chan interface{}, omitHeaders bool) error { // Get the first value. It wil determine the header structure. firstValue, ok := <-c if !ok { - return fmt.Errorf("channel is closed") + return ErrChannelIsClosed } inValue, inType := getConcreteReflectValueAndType(firstValue) // Get the concrete type if err := ensureStructOrPtr(inType); err != nil { @@ -30,8 +35,10 @@ func writeFromChan(writer *SafeCSVWriter, c <-chan interface{}) error { 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 + if !omitHeaders { + if err := writer.Write(csvHeadersLabels); err != nil { + return err + } } write := func(val reflect.Value) error { for j, fieldInfo := range inInnerStructInfo.Fields { @@ -63,7 +70,7 @@ func writeFromChan(writer *SafeCSVWriter, c <-chan interface{}) error { return writer.Error() } -func writeTo(writer *SafeCSVWriter, in interface{}, omitHeaders bool) error { +func writeTo(writer CSVWriter, 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 @@ -138,6 +145,21 @@ func getInnerField(outInner reflect.Value, outInnerWasPointer bool, index []int) } oi = outInner.Elem() } + + if oi.Kind() == reflect.Slice || oi.Kind() == reflect.Array { + i := index[0] + + if i >= oi.Len() { + return "", nil + } + + item := oi.Index(i) + if len(index) > 1 { + return getInnerField(item, false, index[1:]) + } + return getFieldAsString(item) + } + // because pointers can be nil need to recurse one index at a time and perform nil check if len(index) > 1 { nextField := oi.Field(index[0]) diff --git a/vendor/github.com/gocarina/gocsv/go.mod b/vendor/github.com/gocarina/gocsv/go.mod deleted file mode 100644 index c746a5a..0000000 --- a/vendor/github.com/gocarina/gocsv/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/gocarina/gocsv - -go 1.13 diff --git a/vendor/github.com/gocarina/gocsv/reflect.go b/vendor/github.com/gocarina/gocsv/reflect.go index dfc63b3..309f80b 100644 --- a/vendor/github.com/gocarina/gocsv/reflect.go +++ b/vendor/github.com/gocarina/gocsv/reflect.go @@ -1,7 +1,9 @@ package gocsv import ( + "fmt" "reflect" + "strconv" "strings" "sync" ) @@ -17,9 +19,10 @@ type structInfo struct { // 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 + keys []string + omitEmpty bool + IndexChain []int + defaultValue string } func (f fieldInfo) getFirstKey() string { @@ -64,18 +67,17 @@ func getFieldInfos(rType reflect.Type, parentIndexChain []int) []fieldInfo { var cpy = make([]int, len(parentIndexChain)) copy(cpy, parentIndexChain) indexChain := append(cpy, i) - // if the field is a pointer to a struct, follow the pointer then create fieldinfo for each field if field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct { - // unless it implements marshalText or marshalCSV. Structs that implement this + // Structs that implement any of the text or CSV marshaling methods // should result in one value and not have their fields exposed - if !(canMarshal(field.Type.Elem())) { + if !(canMarshal(field.Type.Elem()) || canMarshal(field.Type)) { fieldsList = append(fieldsList, getFieldInfos(field.Type.Elem(), indexChain)...) } } // if the field is a struct, create a fieldInfo for each of its fields if field.Type.Kind() == reflect.Struct { - // unless it implements marshalText or marshalCSV. Structs that implement this + // Structs that implement any of the text or CSV marshaling methods // should result in one value and not have their fields exposed if !(canMarshal(field.Type)) { fieldsList = append(fieldsList, getFieldInfos(field.Type, indexChain)...) @@ -87,26 +89,91 @@ func getFieldInfos(rType reflect.Type, parentIndexChain []int) []fieldInfo { continue } - fieldInfo := fieldInfo{IndexChain: indexChain} + currFieldInfo := fieldInfo{IndexChain: indexChain} fieldTag := field.Tag.Get(TagName) fieldTags := strings.Split(fieldTag, TagSeparator) filteredTags := []string{} for _, fieldTagEntry := range fieldTags { - if fieldTagEntry != "omitempty" { - filteredTags = append(filteredTags, normalizeName(fieldTagEntry)) + trimmedFieldTagEntry := strings.TrimSpace(fieldTagEntry) // handles cases like `csv:"foo, omitempty, default=test"` + if trimmedFieldTagEntry == "omitempty" { + currFieldInfo.omitEmpty = true + } else if strings.HasPrefix(trimmedFieldTagEntry, "default=") { + currFieldInfo.defaultValue = strings.TrimPrefix(trimmedFieldTagEntry, "default=") } else { - fieldInfo.omitEmpty = true + filteredTags = append(filteredTags, normalizeName(trimmedFieldTagEntry)) } } if len(filteredTags) == 1 && filteredTags[0] == "-" { continue } else if len(filteredTags) > 0 && filteredTags[0] != "" { - fieldInfo.keys = filteredTags + currFieldInfo.keys = filteredTags + } else { + currFieldInfo.keys = []string{normalizeName(field.Name)} + } + + if field.Type.Kind() == reflect.Slice || field.Type.Kind() == reflect.Array { + var arrayLength = -1 + if arrayTag, ok := field.Tag.Lookup(TagName + "[]"); ok { + arrayLength, _ = strconv.Atoi(arrayTag) + } + + // When the field is a slice/array of structs, create a fieldInfo for each index and each field + if field.Type.Elem().Kind() == reflect.Struct { + fieldInfos := getFieldInfos(field.Type.Elem(), []int{}) + + for idx := 0; idx < arrayLength; idx++ { + // copy index chain and append array index + var cpy2 = make([]int, len(indexChain)) + copy(cpy2, indexChain) + arrayIndexChain := append(cpy2, idx) + for _, childFieldInfo := range fieldInfos { + // copy array index chain and append array index + var cpy3 = make([]int, len(arrayIndexChain)) + copy(cpy3, arrayIndexChain) + + arrayFieldInfo := fieldInfo{ + IndexChain: append(cpy3, childFieldInfo.IndexChain...), + omitEmpty: childFieldInfo.omitEmpty, + defaultValue: childFieldInfo.defaultValue, + } + + // create cartesian product of keys + // eg: array field keys x struct field keys + for _, akey := range currFieldInfo.keys { + for _, fkey := range childFieldInfo.keys { + arrayFieldInfo.keys = append(arrayFieldInfo.keys, normalizeName(fmt.Sprintf("%s[%d].%s", akey, idx, fkey))) + } + } + + fieldsList = append(fieldsList, arrayFieldInfo) + } + } + } else if arrayLength > 0 { + // When the field is a slice/array of primitives, create a fieldInfo for each index + for idx := 0; idx < arrayLength; idx++ { + // copy index chain and append array index + var cpy2 = make([]int, len(indexChain)) + copy(cpy2, indexChain) + + arrayFieldInfo := fieldInfo{ + IndexChain: append(cpy2, idx), + omitEmpty: currFieldInfo.omitEmpty, + defaultValue: currFieldInfo.defaultValue, + } + + for _, akey := range currFieldInfo.keys { + arrayFieldInfo.keys = append(arrayFieldInfo.keys, normalizeName(fmt.Sprintf("%s[%d]", akey, idx))) + } + + fieldsList = append(fieldsList, arrayFieldInfo) + } + } else { + fieldsList = append(fieldsList, currFieldInfo) + } } else { - fieldInfo.keys = []string{normalizeName(field.Name)} + fieldsList = append(fieldsList, currFieldInfo) } - fieldsList = append(fieldsList, fieldInfo) } return fieldsList } diff --git a/vendor/github.com/gocarina/gocsv/safe_csv.go b/vendor/github.com/gocarina/gocsv/safe_csv.go index 4b2882f..858b078 100644 --- a/vendor/github.com/gocarina/gocsv/safe_csv.go +++ b/vendor/github.com/gocarina/gocsv/safe_csv.go @@ -6,6 +6,12 @@ import ( "sync" ) +type CSVWriter interface { + Write(row []string) error + Flush() + Error() error +} + type SafeCSVWriter struct { *csv.Writer m sync.Mutex diff --git a/vendor/github.com/gocarina/gocsv/types.go b/vendor/github.com/gocarina/gocsv/types.go index a5dda89..537151a 100644 --- a/vendor/github.com/gocarina/gocsv/types.go +++ b/vendor/github.com/gocarina/gocsv/types.go @@ -182,6 +182,7 @@ func toFloat(in interface{}) (float64, error) { if s == "" { return 0, nil } + s = strings.Replace(s, ",", ".", -1) return strconv.ParseFloat(s, 64) case reflect.Bool: if inValue.Bool() { @@ -279,6 +280,10 @@ func setField(field reflect.Value, value string, omitEmpty bool) error { } field.SetFloat(f) case reflect.Slice, reflect.Struct: + if value == "" { + return nil + } + err := json.Unmarshal([]byte(value), field.Addr().Interface()) if err != nil { return err @@ -300,8 +305,6 @@ func getFieldAsString(field reflect.Value) (str string, err error) { return "", nil } return getFieldAsString(field.Elem()) - case reflect.String: - return field.String(), nil default: // Check if field is go native type switch field.Interface().(type) { @@ -363,6 +366,15 @@ func getFieldAsString(field reflect.Value) (str string, err error) { if err != nil { return str, err } + case reflect.Slice: + fallthrough + case reflect.Array: + b, err := json.Marshal(field.Addr().Interface()) + if err != nil { + return str, err + } + + str = string(b) } } else { return str, nil @@ -376,11 +388,13 @@ func getFieldAsString(field reflect.Value) (str string, err error) { // Un/serializations helpers func canMarshal(t reflect.Type) bool { - // unless it implements marshalText or marshalCSV. Structs that implement this + // Structs that implement any of the text or CSV marshaling methods // should result in one value and not have their fields exposed _, canMarshalText := t.MethodByName("MarshalText") _, canMarshalCSV := t.MethodByName("MarshalCSV") - return canMarshalCSV || canMarshalText + _, canUnmarshalText := t.MethodByName("UnmarshalText") + _, canUnmarshalCSV := t.MethodByName("UnmarshalCSV") + return canMarshalCSV || canMarshalText || canUnmarshalText || canUnmarshalCSV } func unmarshall(field reflect.Value, value string) error { diff --git a/vendor/github.com/gocarina/gocsv/unmarshaller.go b/vendor/github.com/gocarina/gocsv/unmarshaller.go index 87e6a8f..50d528e 100644 --- a/vendor/github.com/gocarina/gocsv/unmarshaller.go +++ b/vendor/github.com/gocarina/gocsv/unmarshaller.go @@ -2,7 +2,6 @@ package gocsv import ( "encoding/csv" - "errors" "fmt" "reflect" ) @@ -15,6 +14,7 @@ type Unmarshaller struct { MismatchedHeaders []string MismatchedStructFields []string outType reflect.Type + out interface{} } // NewUnmarshaller creates an unmarshaller from a csv.Reader and a struct. @@ -66,7 +66,7 @@ func validate(um *Unmarshaller, s interface{}, headers []string) error { } structInfo := getStructInfo(concreteType) // Get struct info to get CSV annotations. if len(structInfo.Fields) == 0 { - return errors.New("no csv struct tags found") + return ErrNoStructTags } csvHeadersLabels := make([]*fieldInfo, len(headers)) // Used to store the corresponding header <-> position in CSV headerCount := map[string]int{} @@ -91,6 +91,7 @@ func validate(um *Unmarshaller, s interface{}, headers []string) error { um.fieldInfoMap = csvHeadersLabels um.MismatchedHeaders = mismatchHeaderFields(structInfo.Fields, headers) um.MismatchedStructFields = mismatchStructFields(structInfo.Fields, headers) + um.out = s return nil } @@ -116,3 +117,18 @@ func (um *Unmarshaller) unmarshalRow(row []string, unmatched map[string]string) } return outValue.Interface(), nil } + +// RenormalizeHeaders will remap the header names based on the headerNormalizer. +// This can be used to map a CSV to a struct where the CSV header names do not match in the file but a mapping is known +func (um *Unmarshaller) RenormalizeHeaders(headerNormalizer func([]string) []string) error { + headers := um.Headers + if headerNormalizer != nil { + headers = headerNormalizer(headers) + } + err := validate(um, um.out, headers) + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/spf13/pflag/go.mod b/vendor/github.com/spf13/pflag/go.mod deleted file mode 100644 index b2287ee..0000000 --- a/vendor/github.com/spf13/pflag/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/spf13/pflag - -go 1.12 diff --git a/vendor/github.com/spf13/pflag/go.sum b/vendor/github.com/spf13/pflag/go.sum deleted file mode 100644 index e69de29..0000000 -- cgit v1.2.3