From d21f39eeebd3586e7faf4d83c7a8e12b6e04c82e Mon Sep 17 00:00:00 2001 From: Victor Häggqvist Date: Tue, 21 Apr 2020 09:34:44 +0200 Subject: replace ini --- vendor/github.com/gocarina/gocsv/decode.go | 191 ++++++++++++++++++++++++----- 1 file changed, 160 insertions(+), 31 deletions(-) (limited to 'vendor/github.com/gocarina/gocsv/decode.go') diff --git a/vendor/github.com/gocarina/gocsv/decode.go b/vendor/github.com/gocarina/gocsv/decode.go index 980d7f7..a832e84 100644 --- a/vendor/github.com/gocarina/gocsv/decode.go +++ b/vendor/github.com/gocarina/gocsv/decode.go @@ -16,26 +16,7 @@ type Decoder interface { // 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() + getCSVRows() ([][]string, error) } type CSVReader interface { @@ -47,6 +28,18 @@ type csvDecoder struct { CSVReader } +func newSimpleDecoderFromReader(r io.Reader) SimpleDecoder { + return csvDecoder{getCSVReader(r)} +} + +// 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 +// directly here. +func NewSimpleDecoderFromCSVReader(r CSVReader) SimpleDecoder { + return csvDecoder{r} +} + func (c csvDecoder) getCSVRows() ([][]string, error) { return c.ReadAll() } @@ -87,7 +80,7 @@ func mismatchHeaderFields(structInfo []fieldInfo, headers []string) []string { return missing } - keyMap := make(map[string]struct{}, 0) + keyMap := make(map[string]struct{}) for _, info := range structInfo { for _, key := range info.keys { keyMap[key] = struct{}{} @@ -115,14 +108,27 @@ 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) + return fmt.Errorf("repeated header name: %v", v) } headerMap[v] = true } return nil } +// apply normalizer func to headers +func normalizeHeaders(headers []string) []string { + out := make([]string, len(headers)) + for i, h := range headers { + out[i] = normalizeName(h) + } + return out +} + func readTo(decoder Decoder, out interface{}) error { + return readToWithErrorHandler(decoder, nil, out) +} + +func readToWithErrorHandler(decoder Decoder, errHandler ErrorHandler, out interface{}) error { outValue, outType := getConcreteReflectValueAndType(out) // Get the concrete type (not pointer) (Slice or Array) if err := ensureOutType(outType); err != nil { return err @@ -146,7 +152,7 @@ func readTo(decoder Decoder, out interface{}) error { return errors.New("no csv struct tags found") } - headers := csvRows[0] + headers := normalizeHeaders(csvRows[0]) body := csvRows[1:] csvHeadersLabels := make(map[int]*fieldInfo, len(outInnerStructInfo.Fields)) // Used to store the correspondance header <-> position in CSV @@ -174,34 +180,66 @@ func readTo(decoder Decoder, out interface{}) error { } } + var withFieldsOK bool + var fieldTypeUnmarshallerWithKeys TypeUnmarshalCSVWithFields + for i, csvRow := range body { + objectIface := reflect.New(outValue.Index(i).Type()).Interface() outInner := createNewOutInner(outInnerWasPointer, outInnerType) for j, csvColumnContent := range csvRow { if fieldInfo, ok := csvHeadersLabels[j]; ok { // Position found accordingly to header name + + if outInner.CanInterface() { + fieldTypeUnmarshallerWithKeys, withFieldsOK = objectIface.(TypeUnmarshalCSVWithFields) + if withFieldsOK { + if err := fieldTypeUnmarshallerWithKeys.UnmarshalCSVWithFields(fieldInfo.getFirstKey(), csvColumnContent); err != nil { + parseError := csv.ParseError{ + Line: i + 2, //add 2 to account for the header & 0-indexing of arrays + Column: j + 1, + Err: err, + } + return &parseError + } + continue + } + } + if err := setInnerField(&outInner, outInnerWasPointer, fieldInfo.IndexChain, csvColumnContent, fieldInfo.omitEmpty); err != nil { // Set field of struct - return &csv.ParseError{ + parseError := csv.ParseError{ Line: i + 2, //add 2 to account for the header & 0-indexing of arrays Column: j + 1, Err: err, } + if errHandler == nil || !errHandler(&parseError) { + return &parseError + } } } } + + if withFieldsOK { + reflectedObject := reflect.ValueOf(objectIface) + outInner = reflectedObject.Elem() + } + outValue.Index(i).Set(outInner) } return nil } func readEach(decoder SimpleDecoder, c interface{}) error { + outValue, outType := getConcreteReflectValueAndType(c) // Get the concrete type (not pointer) + if outType.Kind() != reflect.Chan { + return fmt.Errorf("cannot use %v with type %s, only channel supported", c, outType) + } + defer outValue.Close() + 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() + headers = normalizeHeaders(headers) + outInnerWasPointer, outInnerType := getConcreteContainerInnerType(outType) // Get the concrete inner type (not pointer) (Container<"?">) if err := ensureOutInnerType(outInnerType); err != nil { return err @@ -258,6 +296,89 @@ func readEach(decoder SimpleDecoder, c interface{}) error { return nil } +func readEachWithoutHeaders(decoder SimpleDecoder, c interface{}) error { + 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") + } + + 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 { + fieldInfo := outInnerStructInfo.Fields[j] + 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 +} + +func readToWithoutHeaders(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)+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") + } + + for i, csvRow := range csvRows { + outInner := createNewOutInner(outInnerWasPointer, outInnerType) + for j, csvColumnContent := range csvRow { + fieldInfo := outInnerStructInfo.Fields[j] + if err := setInnerField(&outInner, outInnerWasPointer, fieldInfo.IndexChain, csvColumnContent, fieldInfo.omitEmpty); err != nil { // Set field of struct + return &csv.ParseError{ + Line: i + 1, + Column: j + 1, + Err: err, + } + } + } + outValue.Index(i).Set(outInner) + } + + return nil +} + // Check if the outType is an array or a slice func ensureOutType(outType reflect.Type) error { switch outType.Kind() { @@ -302,9 +423,8 @@ func getCSVFieldPosition(key string, structInfo *structInfo, curHeaderCount int) if field.matchesKey(key) { if matchedFieldCount >= curHeaderCount { return &field - } else { - matchedFieldCount++ } + matchedFieldCount++ } } return nil @@ -320,7 +440,16 @@ func createNewOutInner(outInnerWasPointer bool, outInnerType reflect.Type) refle func setInnerField(outInner *reflect.Value, outInnerWasPointer bool, index []int, value string, omitEmpty bool) error { oi := *outInner if outInnerWasPointer { + // initialize nil pointer + if oi.IsNil() { + setField(oi, "", omitEmpty) + } oi = outInner.Elem() } + // 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]) + return setInnerField(&nextField, nextField.Kind() == reflect.Ptr, index[1:], value, omitEmpty) + } return setField(oi.FieldByIndex(index), value, omitEmpty) } -- cgit v1.2.3