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
114
115
|
package dynamicid
import (
"encoding/csv"
"fmt"
"reflect"
"strconv"
"strings"
)
var (
stringReflectType = reflect.TypeFor[string]()
intReflectType = reflect.TypeFor[int]()
uintReflectType = reflect.TypeFor[uint]()
boolReflectType = reflect.TypeFor[bool]()
)
// UnmarshallCSV record into a struct in-place
func UnmarshallCSV(data string, v any) error {
r := csv.NewReader(strings.NewReader(data))
record, err := r.Read()
if err != nil {
return err
}
s := reflect.ValueOf(v).Elem()
t := s.Type()
if s.NumField() != len(record) {
return &ErrFieldMismatch{s.NumField(), len(record)}
}
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
if t.Field(i).Tag.Get("cid") != "-" {
switch f.Type() {
case stringReflectType:
f.SetString(record[i])
case intReflectType:
v, err := strconv.ParseInt(record[i], 10, 0)
if err != nil {
return err
}
f.SetInt(v)
case uintReflectType:
v, err := strconv.ParseUint(record[i], 10, 0)
if err != nil {
return err
}
f.SetUint(v)
case boolReflectType:
switch record[i] {
case "0":
f.SetBool(false)
case "1":
f.SetBool(true)
default:
return &ErrUnreadable{"boolean", record[i]}
}
default:
return &ErrUnsupportedType{Type: f.Type().String()}
}
}
}
return nil
}
// MarshallCSV from a struct
func MarshallCSV(v any) string {
s := reflect.ValueOf(v)
r := make([]string, 0)
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
switch f.Type() {
case stringReflectType:
r = append(r, f.String())
case intReflectType:
r = append(r, strconv.FormatInt(f.Int(), 10))
case uintReflectType:
r = append(r, strconv.FormatUint(f.Uint(), 10))
case boolReflectType:
if f.Bool() {
r = append(r, "1")
} else {
r = append(r, "0")
}
}
}
b := new(strings.Builder)
w := csv.NewWriter(b)
w.Write(r)
w.Flush()
return b.String()
}
type ErrFieldMismatch struct {
Expected, Found int
}
func (e *ErrFieldMismatch) Error() string {
return fmt.Sprintf("CSV line fields mismatch. Expected %d found %d", e.Expected, e.Found)
}
type ErrUnreadable struct {
Format, Found string
}
func (e *ErrUnreadable) Error() string {
return fmt.Sprintf("Unreadable value as %s. Found %s", e.Format, e.Found)
}
type ErrUnsupportedType struct {
Type string
}
func (e *ErrUnsupportedType) Error() string {
return "Unsupported type: " + e.Type
}
|