Skip to content

Commit e516fe9

Browse files
committed
witch to idiomatic error handling
1 parent 3e9ce2d commit e516fe9

9 files changed

Lines changed: 443 additions & 198 deletions

File tree

csvw/column/column.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ type Column struct {
1616
Null []string
1717
}
1818

19-
func New(index int, jsonCol map[string]interface{}) *Column {
19+
func New(index int, jsonCol map[string]interface{}) (*Column, error) {
2020
var (
2121
name string = ""
2222
purl string = ""
@@ -56,13 +56,18 @@ func New(index int, jsonCol map[string]interface{}) *Column {
5656
} else {
5757
canonicalName = name
5858
}
59-
return &Column{
59+
dt, err := datatype.New(jsonCol)
60+
if err != nil {
61+
return nil, err
62+
}
63+
col := &Column{
6064
Name: name,
6165
CanonicalName: canonicalName,
6266
PropertyUrl: purl,
63-
Datatype: *datatype.New(jsonCol),
67+
Datatype: *dt,
6468
Separator: sep,
6569
Null: null}
70+
return col, nil
6671
}
6772

6873
func (column *Column) ToGo(s string, split bool) (any, error) {
@@ -87,6 +92,10 @@ func (column *Column) ToGo(s string, split bool) (any, error) {
8792
return column.Datatype.ToGo(s)
8893
}
8994

95+
func (column *Column) ToSql(x any) (any, error) {
96+
return column.Datatype.ToSql(x)
97+
}
98+
9099
func (column *Column) ToString(x any) (string, error) {
91100
if x == nil {
92101
return column.Null[0], nil

csvw/column/column_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ func makeCol(jsonString string) Column {
1212
if err != nil {
1313
panic(err)
1414
}
15-
return *New(0, result)
15+
col, err := New(0, result)
16+
if err != nil {
17+
panic(err)
18+
}
19+
return *col
1620
}
1721

1822
func TestColumn_CanonicalName(t *testing.T) {

csvw/dataset/dataset.go

Lines changed: 82 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@ package dataset
33
import (
44
"database/sql"
55
"encoding/json"
6+
"errors"
7+
"fmt"
68
"gocldf/csvw/table"
7-
"gocldf/db"
89
"os"
910
"path/filepath"
1011
"slices"
1112
"strings"
12-
13-
_ "github.com/mattn/go-sqlite3"
1413
)
1514

1615
type Dataset struct {
@@ -19,16 +18,16 @@ type Dataset struct {
1918
Tables map[string]*table.Table
2019
}
2120

22-
func New(md_path string) *Dataset {
21+
func New(md_path string) (*Dataset, error) {
2322
data, err := os.ReadFile(md_path)
2423
if err != nil {
25-
panic(err)
24+
return nil, err
2625
}
2726
var result map[string]interface{}
2827

2928
err = json.Unmarshal(data, &result)
3029
if err != nil {
31-
panic(err)
30+
return nil, err
3231
}
3332

3433
metadata := make(map[string]interface{}, len(result)-1)
@@ -44,24 +43,28 @@ func New(md_path string) *Dataset {
4443
metadata,
4544
make(map[string]*table.Table)}
4645
for _, value := range result["tables"].([]interface{}) {
47-
tbl := table.New(value.(map[string]interface{}))
46+
tbl, err := table.New(value.(map[string]interface{}))
47+
if err != nil {
48+
return nil, err
49+
}
4850
res.Tables[tbl.CanonicalName] = tbl
4951
}
50-
return &res
52+
return &res, nil
5153
}
5254

53-
func (dataset *Dataset) LoadData() {
55+
func (dataset *Dataset) LoadData() error {
5456
results := make(chan table.TableRead, len(dataset.Tables))
5557
for _, tbl := range dataset.Tables {
5658
go tbl.Read(filepath.Dir(dataset.MetadataPath), results)
5759
}
5860
for i := 0; i < len(dataset.Tables); i++ {
5961
tableRead := <-results
6062
if tableRead.Err != nil {
61-
panic(tableRead.Err)
63+
return tableRead.Err
6264
}
6365
}
6466
close(results)
67+
return nil
6568
}
6669

6770
func (dataset *Dataset) UrlToTable() map[string]*table.Table {
@@ -80,29 +83,7 @@ func (dataset *Dataset) UrlToCanonicalName() map[string]string {
8083
return res
8184
}
8285

83-
func (dataset *Dataset) SqlSchema() string {
84-
var (
85-
res []string
86-
orderedTables = dataset.OrderedTables()
87-
urlToName = dataset.UrlToCanonicalName()
88-
urlToTable = dataset.UrlToTable()
89-
)
90-
for _, url := range orderedTables {
91-
tbl, ok := dataset.Tables[urlToName[url]]
92-
if ok {
93-
res = append(res, tbl.SqlCreate(urlToTable))
94-
}
95-
}
96-
for _, url := range orderedTables {
97-
tbl, ok := dataset.Tables[urlToName[url]]
98-
if ok {
99-
res = append(res, tbl.SqlCreateAssociationTables(urlToTable))
100-
}
101-
}
102-
return strings.Join(res, "\n")
103-
}
104-
105-
func (dataset *Dataset) OrderedTables() []string {
86+
func (dataset *Dataset) orderedTables() (map[string]*table.Table, error) {
10687
var urlToName = dataset.UrlToCanonicalName()
10788
// Determine the order in which to create the tables
10889
tables := []string{}
@@ -114,7 +95,7 @@ func (dataset *Dataset) OrderedTables() []string {
11495
for len(tables) > 0 {
11596
j++
11697
if j > 100 {
117-
panic("there may be cyclic dependencies between tables")
98+
return nil, errors.New("there may be cyclic dependencies between tables")
11899
}
119100
// We loop over all tables that have not been ordered yet, trying to find one with
120101
// only fks to already ordered tables.
@@ -138,45 +119,86 @@ func (dataset *Dataset) OrderedTables() []string {
138119
break
139120
}
140121
} else {
141-
panic("table not found")
122+
return nil, errors.New("table not found")
142123
}
143124
}
144125
if delIndex >= 0 {
145126
tables = slices.Delete(tables, delIndex, delIndex+1)
146127
}
147128
}
148-
return orderedTables
129+
orderedTableMap := make(map[string]*table.Table, len(orderedTables))
130+
for _, url := range orderedTables {
131+
tbl, ok := dataset.Tables[urlToName[url]]
132+
if ok {
133+
orderedTableMap[url] = tbl
134+
} else {
135+
return orderedTableMap, fmt.Errorf("table %s not found", url)
136+
}
137+
}
138+
return orderedTableMap, nil
149139
}
150140

151-
func (dataset *Dataset) ToSqlite(db_path string) {
152-
err := db.WithDatabase(db_path, func(database *sql.DB) error {
153-
_, err := database.Exec(dataset.SqlSchema())
141+
func (dataset *Dataset) sqlSchema() (string, error) {
142+
var (
143+
res []string
144+
urlToTable = dataset.UrlToTable()
145+
)
146+
orderedTableMap, err := dataset.orderedTables()
147+
if err != nil {
148+
return "", err
149+
}
150+
151+
for _, tbl := range orderedTableMap {
152+
schema, err := tbl.SqlCreate(urlToTable)
154153
if err != nil {
155-
return err
154+
return "", err
155+
} else {
156+
res = append(res, schema)
157+
}
158+
}
159+
for _, tbl := range orderedTableMap {
160+
for _, fk := range tbl.ManyToMany() {
161+
res = append(res, tbl.SqlCreateAssociationTable(*fk, urlToTable))
156162
}
163+
}
164+
return strings.Join(res, "\n"), nil
165+
}
157166

158-
orderedTables := dataset.OrderedTables()
159-
urlToName := dataset.UrlToCanonicalName()
160-
urlToTable := dataset.UrlToTable()
167+
type TableData struct {
168+
TableName string
169+
ColNames []string
170+
Rows [][]any
171+
}
161172

162-
db.WithTransaction(database, func(tx *sql.Tx) {
163-
for _, url := range orderedTables {
164-
tbl, ok := dataset.Tables[urlToName[url]]
165-
if ok {
166-
tbl.SqlInsert(tx)
167-
}
168-
}
173+
// Function ToSqlite returns the data necessary to load the dataset into a SQLite database.
174+
func (dataset *Dataset) ToSqlite(tx *sql.Tx) (string, []TableData, error) {
175+
var tableData = []TableData{}
169176

170-
for _, url := range orderedTables {
171-
tbl, ok := dataset.Tables[urlToName[url]]
172-
if ok {
173-
tbl.SqlInsertAssociationTables(tx, urlToTable)
174-
}
175-
}
176-
})
177-
return err
178-
}, true)
177+
schema, err := dataset.sqlSchema()
178+
if err != nil {
179+
return "", tableData, err
180+
}
181+
orderedTables, err := dataset.orderedTables()
179182
if err != nil {
180-
return
183+
return "", tableData, err
184+
}
185+
urlToTable := dataset.UrlToTable()
186+
187+
for _, tbl := range orderedTables {
188+
rows, colNames, err := tbl.RowsToSql()
189+
if err != nil {
190+
return "", tableData, err
191+
}
192+
tableData = append(tableData, TableData{tbl.CanonicalName, colNames, rows})
193+
}
194+
for _, tbl := range orderedTables {
195+
for _, fk := range tbl.ManyToMany() {
196+
rows, tableName, colNames, err := tbl.AssociationTableRowsToSql(fk, urlToTable)
197+
if err != nil {
198+
return "", tableData, err
199+
}
200+
tableData = append(tableData, TableData{tableName, colNames, rows})
201+
}
181202
}
203+
return schema, tableData, nil
182204
}

csvw/datatype/datatype.go

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package datatype
33
import (
44
"encoding/json"
55
"errors"
6+
"fmt"
7+
"net/url"
68
"regexp"
79
"slices"
810
"strconv"
@@ -58,6 +60,7 @@ type BaseType struct {
5860
}
5961

6062
var (
63+
// Boolean: ToGo returns an object of type bool.
6164
Boolean = BaseType{
6265
GetDerivedDescription: func(dtProps map[string]any) (map[string]any, error) {
6366
val, ok := dtProps["format"]
@@ -90,6 +93,7 @@ var (
9093
return 0, nil
9194
},
9295
}
96+
// String ------------------------------------------------------------------------------
9397
String = BaseType{
9498
GetDerivedDescription: func(dtProps map[string]any) (map[string]any, error) {
9599
val, ok := dtProps["format"]
@@ -111,25 +115,38 @@ var (
111115
return x.(string), nil
112116
},
113117
SqlType: "TEXT",
114-
ToSql: func(dt *Datatype, x any) (any, error) {
115-
return x.(string), nil
116-
},
118+
ToSql: func(dt *Datatype, x any) (any, error) { return x.(string), nil },
117119
}
120+
// AnyURI: ToGo returns an object of type url.URL
118121
AnyURI = BaseType{
119122
GetDerivedDescription: func(dtProps map[string]any) (map[string]any, error) {
120123
return map[string]any{}, nil
121124
},
122125
ToGo: func(dt *Datatype, s string) (any, error) {
123-
return s, nil
126+
u, err := url.Parse(s)
127+
if err != nil {
128+
return nil, err
129+
}
130+
// We don't want the rather lax parsing of url.Parse.
131+
/*
132+
_, err = url.ParseRequestURI(s)
133+
if err != nil {
134+
return nil, err
135+
}
136+
*/
137+
return u, nil
124138
},
125139
ToString: func(dt *Datatype, x any) (string, error) {
126-
return x.(string), nil
140+
u := x.(*url.URL)
141+
return u.String(), nil
127142
},
128143
SqlType: "TEXT",
129144
ToSql: func(dt *Datatype, x any) (any, error) {
130-
return x.(string), nil
145+
u := x.(*url.URL)
146+
return u.String(), nil
131147
},
132148
}
149+
// Integer ----------------------------------------------------------------------------
133150
Integer = BaseType{
134151
GetDerivedDescription: func(dtProps map[string]any) (map[string]any, error) {
135152
return map[string]any{}, nil
@@ -161,7 +178,7 @@ var (
161178
return val, nil
162179
},
163180
ToString: func(dt *Datatype, x any) (string, error) {
164-
return x.(string), nil
181+
return fmt.Sprintf("%g", x.(float64)), nil
165182
},
166183
SqlType: "REAL",
167184
ToSql: func(dt *Datatype, x any) (any, error) {
@@ -206,7 +223,7 @@ var BaseTypes = map[string]BaseType{
206223
"json": Json,
207224
}
208225

209-
func New(jsonCol map[string]interface{}) *Datatype {
226+
func New(jsonCol map[string]interface{}) (*Datatype, error) {
210227
base := "string"
211228
dtProps := map[string]any{}
212229

@@ -220,12 +237,11 @@ func New(jsonCol map[string]interface{}) *Datatype {
220237
dtProps = val.(map[string]any)
221238
}
222239
}
223-
//fmt.Println(base)
224240
dd, err := BaseTypes[base].GetDerivedDescription(dtProps)
225241
if err != nil {
226-
panic(err)
242+
return nil, err
227243
}
228-
return &Datatype{Base: base, DerivedDescription: dd}
244+
return &Datatype{Base: base, DerivedDescription: dd}, nil
229245
}
230246

231247
func (dt *Datatype) ToString(val any) (string, error) {

0 commit comments

Comments
 (0)