-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathencoder.go
More file actions
113 lines (92 loc) · 2.66 KB
/
encoder.go
File metadata and controls
113 lines (92 loc) · 2.66 KB
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package csvx
import (
"encoding"
"fmt"
"reflect"
"strconv"
)
type CustomEncoderFunc func(val interface{}) (string, error)
type Encoder struct {
FieldsMap map[string]int
FloatFmt byte
NullText string
BoolTrueText, BoolFalseText string
// map[csv tag name]encoder func
CustomEncoderMap map[string]CustomEncoderFunc
}
func NewEncoder(fields []string) *Encoder {
fieldsMap := make(map[string]int)
for i, field := range fields {
fieldsMap[field] = i
}
return &Encoder{fieldsMap, 'f', "null", "true", "false", nil}
}
func (e *Encoder) Encode(target interface{}) ([]string, error) {
fieldsMapLen := len(e.FieldsMap)
if fieldsMapLen == 0 {
return nil, fmt.Errorf("no fields selected for encoding")
}
records := make([]string, fieldsMapLen)
onFieldFound := func(fieldCsvTag string, field reflect.Value) error {
var err error
idx, ok := e.FieldsMap[fieldCsvTag]
if !ok {
// field not requested for scanning
return nil
}
records[idx], err = e.toString(fieldCsvTag, field)
if err != nil {
return fmt.Errorf("csvx: error getting string from field. Field: %q, field index: %d. Error: %s", fieldCsvTag, idx, err)
}
return nil
}
err := traverseFields(target, false, onFieldFound)
if err != nil {
return nil, err
}
return records, nil
}
func (e *Encoder) toString(fieldCsvTag string, field reflect.Value) (string, error) {
kind := field.Kind()
if kind == reflect.Pointer {
if field.IsNil() {
return e.NullText, nil
}
return e.toString(fieldCsvTag, field.Elem())
}
if e.CustomEncoderMap != nil {
customFunc, ok := e.CustomEncoderMap[fieldCsvTag]
if ok {
return customFunc(field.Interface())
}
}
switch kind {
case reflect.String:
return field.String(), nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(field.Int(), 10), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.FormatUint(field.Uint(), 10), nil
case reflect.Float32, reflect.Float64:
return strconv.FormatFloat(field.Float(), e.FloatFmt, -1, 64), nil
case reflect.Bool:
val := field.Bool()
if val {
return e.BoolTrueText, nil
}
return e.BoolFalseText, nil
case reflect.Struct:
val := field.Interface()
v, ok := val.(encoding.TextMarshaler)
if !ok {
return "", fmt.Errorf("encoding not implemented for kind %q (encoding.TextMarshaler not implemented, implement it to marshal this field)", kind)
}
text, err := v.MarshalText()
if err != nil {
return "", err
}
return string(text), nil
default:
return "", fmt.Errorf("encoding not implemented for kind %q", kind)
}
}