Skip to content

Commit f633ee1

Browse files
committed
witch to idiomatic error handling
1 parent 75f66ff commit f633ee1

5 files changed

Lines changed: 183 additions & 162 deletions

File tree

csvw/column/column.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,27 +65,31 @@ func New(index int, jsonCol map[string]interface{}) *Column {
6565
Null: null}
6666
}
6767

68-
func (column *Column) ToGo(s string, split bool) any {
68+
func (column *Column) ToGo(s string, split bool) (any, error) {
6969
if slices.Contains(column.Null, s) {
7070
if split && column.Separator != "" {
71-
return make([]string, 0)
71+
return make([]string, 0), nil
7272
}
73-
return nil
73+
return nil, nil
7474
}
7575
if split && column.Separator != "" {
7676
fields := strings.Split(s, column.Separator)
7777
res := make([]string, len(fields))
7878
for i, field := range fields {
79-
res[i] = column.ToGo(field, false).(string)
79+
val, err := column.ToGo(field, false)
80+
if err != nil {
81+
return nil, err
82+
}
83+
res[i] = val.(string)
8084
}
81-
return res
85+
return res, nil
8286
}
8387
return column.Datatype.ToGo(s)
8488
}
8589

86-
func (column *Column) ToString(x any) string {
90+
func (column *Column) ToString(x any) (string, error) {
8791
if x == nil {
88-
return column.Null[0]
92+
return column.Null[0], nil
8993
}
9094
return column.Datatype.ToString(x)
9195
}

csvw/column/column_test.go

Lines changed: 30 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,46 +16,39 @@ func makeCol(jsonString string) Column {
1616
}
1717

1818
func TestColumn_CanonicalName(t *testing.T) {
19-
col := makeCol(`{"name":"The Name"}`)
20-
want := "The Name"
21-
if want != col.CanonicalName {
22-
t.Errorf(`problem`)
23-
}
24-
col = makeCol(`{"name":"The Name", "propertyUrl": "http://cldf.clld.org/#prop"}`)
25-
want = "cldf_prop"
26-
if want != col.CanonicalName {
27-
t.Errorf(`problem: %q vs %q`, want, col.CanonicalName)
28-
}
29-
col = makeCol(`{}`)
30-
want = "Col_1"
31-
if want != col.CanonicalName {
32-
t.Errorf(`problem: %q vs %q`, want, col.CanonicalName)
19+
var tests = []struct {
20+
jsonCol string
21+
input string
22+
}{
23+
{`{"name":"The Name"}`, "The Name"},
24+
{`{"name":"The Name", "propertyUrl": "http://cldf.clld.org/#prop"}`, "cldf_prop"},
25+
{`{}`, "Col_1"},
26+
}
27+
for _, tt := range tests {
28+
t.Run("CanonicalName", func(t *testing.T) {
29+
col := makeCol(tt.jsonCol)
30+
if tt.input != col.CanonicalName {
31+
t.Errorf(`problem: %q vs %q`, tt.input, col.CanonicalName)
32+
}
33+
})
3334
}
3435
}
3536

3637
func TestColumn_Datatype(t *testing.T) {
37-
col := makeCol(`{"name": "The Name"}`)
38-
want := "string"
39-
if want != col.Datatype.Base {
40-
t.Errorf(`problem`)
41-
}
42-
col = makeCol(`{"datatype": "boolean"}`)
43-
want = "boolean"
44-
if want != col.Datatype.Base {
45-
t.Errorf(`problem: %q vs %q`, want, col.CanonicalName)
46-
}
47-
col = makeCol(`{"datatype": {"base": "boolean"}}`)
48-
want = "boolean"
49-
if want != col.Datatype.Base {
50-
t.Errorf(`problem: %q vs %q`, want, col.CanonicalName)
38+
var tests = []struct {
39+
jsonCol string
40+
input string
41+
}{
42+
{`{}`, "string"},
43+
{`{"datatype": "boolean"}`, "boolean"},
44+
{`{"datatype": {"base": "boolean"}}`, "boolean"},
45+
}
46+
for _, tt := range tests {
47+
t.Run("Datatype", func(t *testing.T) {
48+
col := makeCol(tt.jsonCol)
49+
if tt.input != col.Datatype.Base {
50+
t.Errorf(`problem: %q vs %q`, tt.input, col.Datatype.Base)
51+
}
52+
})
5153
}
5254
}
53-
54-
func TestColumn_RoundtripValue(t *testing.T) {
55-
col := makeCol(`{"datatype": {"base": "boolean","format":"yes|no"}}`)
56-
want := "yes"
57-
if want != col.ToString(col.ToGo("yes", true)) {
58-
t.Errorf(`problem: %q vs %q`, want, col.CanonicalName)
59-
}
60-
61-
}

csvw/datatype/datatype.go

Lines changed: 80 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package datatype
22

33
import (
44
"encoding/json"
5-
"fmt"
5+
"errors"
66
"regexp"
77
"slices"
88
"strconv"
@@ -50,167 +50,150 @@ type BaseType struct {
5050
// GetDerivedDescription is called when instantiating a Datatype object.
5151
// The result is stored as DerivedDescription member of the Datatype and can be
5252
// accessed from ToGo and ToString via the Datatype passed as first argument.
53-
GetDerivedDescription func(map[string]any) map[string]any
54-
ToGo func(*Datatype, string) any
55-
ToString func(*Datatype, any) string
53+
GetDerivedDescription func(map[string]any) (map[string]any, error)
54+
ToGo func(*Datatype, string) (any, error)
55+
ToString func(*Datatype, any) (string, error)
5656
SqlType string
57-
ToSql func(*Datatype, any) any
57+
ToSql func(*Datatype, any) (any, error)
5858
}
5959

6060
var (
6161
Boolean = BaseType{
62-
GetDerivedDescription: func(dtProps map[string]any) map[string]any {
62+
GetDerivedDescription: func(dtProps map[string]any) (map[string]any, error) {
6363
val, ok := dtProps["format"]
6464
if ok {
6565
yesno := strings.Split(val.(string), "|")
66-
return map[string]any{"true": []string{yesno[0]}, "false": []string{yesno[1]}}
66+
return map[string]any{"true": []string{yesno[0]}, "false": []string{yesno[1]}}, nil
6767
}
68-
return map[string]any{"true": []string{"true", "1"}, "false": []string{"false", "0"}}
68+
return map[string]any{"true": []string{"true", "1"}, "false": []string{"false", "0"}}, nil
6969
},
70-
ToGo: func(dt *Datatype, s string) any {
70+
ToGo: func(dt *Datatype, s string) (any, error) {
7171
if slices.Contains(dt.DerivedDescription["true"].([]string), s) {
72-
return true
73-
} else if slices.Contains(dt.DerivedDescription["false"].([]string), s) {
74-
return false
72+
return true, nil
7573
}
76-
panic("Invalid value for datatype")
74+
if slices.Contains(dt.DerivedDescription["false"].([]string), s) {
75+
return false, nil
76+
}
77+
return nil, errors.New("invalid value")
7778
},
78-
ToString: func(dt *Datatype, x any) string {
79+
ToString: func(dt *Datatype, x any) (string, error) {
7980
if x.(bool) {
80-
return dt.DerivedDescription["true"].([]string)[0]
81+
return dt.DerivedDescription["true"].([]string)[0], nil
8182
}
82-
return dt.DerivedDescription["false"].([]string)[0]
83+
return dt.DerivedDescription["false"].([]string)[0], nil
8384
},
8485
SqlType: "INTEGER",
85-
ToSql: func(dt *Datatype, x any) any {
86-
if x == nil {
87-
return nil
88-
}
86+
ToSql: func(dt *Datatype, x any) (any, error) {
8987
if x.(bool) {
90-
return 1
88+
return 1, nil
9189
}
92-
return 0
90+
return 0, nil
9391
},
9492
}
9593
String = BaseType{
96-
GetDerivedDescription: func(dtProps map[string]any) map[string]any {
94+
GetDerivedDescription: func(dtProps map[string]any) (map[string]any, error) {
9795
val, ok := dtProps["format"]
9896
if ok {
9997
// FIXME: must make sure regex is anchored on both sides! I.e. wrap in "^$" if necessary.
100-
return map[string]any{"regex": regexp.MustCompile(val.(string))}
98+
return map[string]any{"regex": regexp.MustCompile(val.(string))}, nil
10199
}
102-
return map[string]any{"regex": nil}
100+
return map[string]any{"regex": nil}, nil
103101
},
104-
ToGo: func(dt *Datatype, s string) any {
102+
ToGo: func(dt *Datatype, s string) (any, error) {
105103
if dt.DerivedDescription["regex"] != nil {
106104
if !dt.DerivedDescription["regex"].(*regexp.Regexp).MatchString(s) {
107-
panic(fmt.Sprintf("invalid value: %v", s))
105+
return nil, errors.New("invalid value")
108106
}
109107
}
110-
return s
108+
return s, nil
111109
},
112-
ToString: func(dt *Datatype, x any) string {
113-
return x.(string)
110+
ToString: func(dt *Datatype, x any) (string, error) {
111+
return x.(string), nil
114112
},
115113
SqlType: "TEXT",
116-
ToSql: func(dt *Datatype, x any) any {
117-
if x == nil {
118-
return nil
119-
}
120-
return x.(string)
114+
ToSql: func(dt *Datatype, x any) (any, error) {
115+
return x.(string), nil
121116
},
122117
}
123118
AnyURI = BaseType{
124-
GetDerivedDescription: func(dtProps map[string]any) map[string]any {
125-
return map[string]any{}
119+
GetDerivedDescription: func(dtProps map[string]any) (map[string]any, error) {
120+
return map[string]any{}, nil
126121
},
127-
ToGo: func(dt *Datatype, s string) any {
128-
return s
122+
ToGo: func(dt *Datatype, s string) (any, error) {
123+
return s, nil
129124
},
130-
ToString: func(dt *Datatype, x any) string {
131-
return x.(string)
125+
ToString: func(dt *Datatype, x any) (string, error) {
126+
return x.(string), nil
132127
},
133128
SqlType: "TEXT",
134-
ToSql: func(dt *Datatype, x any) any {
135-
if x == nil {
136-
return nil
137-
}
138-
return x.(string)
129+
ToSql: func(dt *Datatype, x any) (any, error) {
130+
return x.(string), nil
139131
},
140132
}
141133
Integer = BaseType{
142-
GetDerivedDescription: func(dtProps map[string]any) map[string]any {
143-
return map[string]any{}
134+
GetDerivedDescription: func(dtProps map[string]any) (map[string]any, error) {
135+
return map[string]any{}, nil
144136
},
145-
ToGo: func(dt *Datatype, s string) any {
137+
ToGo: func(dt *Datatype, s string) (any, error) {
146138
val, err := strconv.Atoi(s)
147-
if err == nil {
148-
return val
139+
if err != nil {
140+
return nil, err
149141
}
150-
panic("Invalid value for integer")
142+
return val, nil
151143
},
152-
ToString: func(dt *Datatype, x any) string {
153-
return x.(string)
144+
ToString: func(dt *Datatype, x any) (string, error) {
145+
return strconv.Itoa(x.(int)), nil
154146
},
155147
SqlType: "INTEGER",
156-
ToSql: func(dt *Datatype, x any) any {
157-
if x == nil {
158-
return nil
159-
}
160-
return x.(int)
148+
ToSql: func(dt *Datatype, x any) (any, error) {
149+
return x.(int), nil
161150
},
162151
}
163152
Decimal = BaseType{
164-
GetDerivedDescription: func(dtProps map[string]any) map[string]any {
165-
return map[string]any{}
153+
GetDerivedDescription: func(dtProps map[string]any) (map[string]any, error) {
154+
return map[string]any{}, nil
166155
},
167-
ToGo: func(dt *Datatype, s string) any {
156+
ToGo: func(dt *Datatype, s string) (any, error) {
168157
val, err := strconv.ParseFloat(s, 64)
169-
if err == nil {
170-
return val
158+
if err != nil {
159+
return nil, err
171160
}
172-
panic("Invalid value for integer")
161+
return val, nil
173162
},
174-
ToString: func(dt *Datatype, x any) string {
175-
return x.(string)
163+
ToString: func(dt *Datatype, x any) (string, error) {
164+
return x.(string), nil
176165
},
177166
SqlType: "REAL",
178-
ToSql: func(dt *Datatype, x any) any {
179-
if x == nil {
180-
return nil
181-
}
182-
return x.(float64)
167+
ToSql: func(dt *Datatype, x any) (any, error) {
168+
return x.(float64), nil
183169
},
184170
}
185171
Json = BaseType{
186-
GetDerivedDescription: func(dtProps map[string]any) map[string]any {
187-
return map[string]any{}
172+
GetDerivedDescription: func(dtProps map[string]any) (map[string]any, error) {
173+
return map[string]any{}, nil
188174
},
189-
ToGo: func(dt *Datatype, s string) any {
175+
ToGo: func(dt *Datatype, s string) (any, error) {
190176
var result any
191177
err := json.Unmarshal([]byte(s), &result)
192178
if err != nil {
193-
panic(fmt.Sprintf("%v: '%v'", err, s))
179+
return nil, err
194180
}
195-
return result
181+
return result, nil
196182
},
197-
ToString: func(dt *Datatype, x any) string {
183+
ToString: func(dt *Datatype, x any) (string, error) {
198184
res, err := json.Marshal(x)
199185
if err != nil {
200-
panic(err)
186+
return "", nil
201187
}
202-
return string(res)
188+
return string(res), nil
203189
},
204190
SqlType: "TEXT",
205-
ToSql: func(dt *Datatype, x any) any {
206-
if x == nil {
207-
return nil
208-
}
191+
ToSql: func(dt *Datatype, x any) (any, error) {
209192
res, err := json.Marshal(x)
210193
if err != nil {
211-
panic(err)
194+
return nil, err
212195
}
213-
return string(res)
196+
return string(res), nil
214197
},
215198
}
216199
)
@@ -238,23 +221,28 @@ func New(jsonCol map[string]interface{}) *Datatype {
238221
}
239222
}
240223
//fmt.Println(base)
241-
return &Datatype{
242-
Base: base,
243-
DerivedDescription: BaseTypes[base].GetDerivedDescription(dtProps)}
224+
dd, err := BaseTypes[base].GetDerivedDescription(dtProps)
225+
if err != nil {
226+
panic(err)
227+
}
228+
return &Datatype{Base: base, DerivedDescription: dd}
244229
}
245230

246-
func (dt *Datatype) ToString(val any) string {
231+
func (dt *Datatype) ToString(val any) (string, error) {
247232
return BaseTypes[dt.Base].ToString(dt, val)
248233
}
249234

250-
func (dt *Datatype) ToGo(s string) any {
235+
func (dt *Datatype) ToGo(s string) (any, error) {
251236
return BaseTypes[dt.Base].ToGo(dt, s)
252237
}
253238

254239
func (dt *Datatype) SqlType() string {
255240
return BaseTypes[dt.Base].SqlType
256241
}
257242

258-
func (dt *Datatype) ToSql(val any) any {
243+
func (dt *Datatype) ToSql(val any) (any, error) {
244+
if val == nil {
245+
return nil, nil
246+
}
259247
return BaseTypes[dt.Base].ToSql(dt, val)
260248
}

0 commit comments

Comments
 (0)