forked from gocarina/gocsv
-
Notifications
You must be signed in to change notification settings - Fork 0
/
config.go
97 lines (83 loc) · 2.95 KB
/
config.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package commando
import (
"reflect"
)
type Config struct {
// Holder is the type of struct to marshal from/unmarshal info.
Holder interface{}
// ErrorHandler is invoked if there's a recoverable error.
//
// If the func returns an error, processing stops. If it returns
// nil, processing continues.
//
// If unset, processing stops on the first error.
ErrorHandler func(error) error
// FailIfUnmatchedStructTags indicates whether it is considered an
// error when there is an unmatched struct tag.
FailIfUnmatchedStructTags bool
// FailIfDoubleHeaderNames indicates whether it is considered an
// error when a header name is repeated in the csv header.
FailIfDoubleHeaderNames bool
// ShouldAlignDuplicateHeadersWithStructFieldOrder indicates
// whether we should align duplicate CSV headers per their
// alignment in the struct definition.
ShouldAlignDuplicateHeadersWithStructFieldOrder bool
}
// validate ensures that a struct was used to create the Unmarshaller, and validates
// CSV headers against the CSV tags in the struct.
func (c *Config) validate(headers []string) (*validConfig, error) {
concreteType := reflect.TypeOf(c.Holder)
if concreteType.Kind() == reflect.Ptr {
concreteType = concreteType.Elem()
}
if err := ensureOutInnerType(concreteType); err != nil {
return nil, err
}
structInfo := getStructInfo(concreteType) // Get struct info to get CSV annotations.
if len(structInfo.Fields) == 0 {
return nil, ErrNoStructTags
}
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]
if fieldInfo := getCSVFieldPosition(csvColumnHeader, structInfo, curHeaderCount); fieldInfo != nil {
csvHeadersLabels[i] = fieldInfo
if c.ShouldAlignDuplicateHeadersWithStructFieldOrder {
curHeaderCount++
headerCount[csvColumnHeader] = curHeaderCount
}
}
}
if c.FailIfUnmatchedStructTags {
if err := maybeMissingStructFields(structInfo.Fields, headers); err != nil {
return nil, err
}
}
if c.FailIfDoubleHeaderNames {
if err := maybeDoubleHeaderNames(headers); err != nil {
return nil, err
}
}
return &validConfig{
Config: *c,
outType: reflect.TypeOf(c.Holder),
headers: headers,
structInfo: structInfo,
fieldInfoMap: csvHeadersLabels,
mismatchedHeaders: mismatchHeaderFields(structInfo.Fields, headers),
mismatchedStructFields: mismatchStructFields(structInfo.Fields, headers),
}, nil
}
// validConfig is a Config which has been validated and contains
// metadata about the output struct type.
type validConfig struct {
Config
outType reflect.Type
// headers is a slice of header names in file order.
headers []string
structInfo *structInfo
fieldInfoMap []*fieldInfo
mismatchedHeaders []string
mismatchedStructFields []string
}