Skip to content

Commit

Permalink
fix time scanning
Browse files Browse the repository at this point in the history
add record type
add scanning to ID
add ability to convert ID types
  • Loading branch information
NoBypass committed Jun 13, 2024
1 parent 3d512ce commit 56c40f1
Show file tree
Hide file tree
Showing 6 changed files with 312 additions and 123 deletions.
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@ go get github.com/NoBypass/surgo
**Make sure that your Go project runs on version 1.22 or later!**

## To-Do
- Fix not parsing back from string to `time.Duration` in scanning
- Allow pointers to structs for parameters
- Allow scanning to a nil pointer
- Improve/Update Docs
- Use SurrealDB variables in ranged IDs
- Improve error messages/errors in general
- [x] Fix not parsing back from string to `time.Duration` in scanning
- [x] Add support for `Record` type
- [x] Parse surreal record id to `ID` type
- [x] Implement converting ID types
- [ ] Implement converting struct to query insertable string
- [ ] Allow pointers to structs for parameters
- [ ] Allow scanning to a nil pointer
- [ ] Improve/Update Docs
- [ ] Use SurrealDB variables in ranged IDs
- [ ] Improve error messages/errors in general

## Connecting to a database
Connect to a database and get a DB object and an error.
Expand Down
81 changes: 0 additions & 81 deletions common.go

This file was deleted.

62 changes: 31 additions & 31 deletions parse_params_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@ import (
"time"
)

type MockQueryAgent struct {
type mockQueryAgent struct {
mock.Mock
}

func (mqa *MockQueryAgent) Query(sql string, vars any) (any, error) {
func (mqa *mockQueryAgent) Query(sql string, vars any) (any, error) {
args := mqa.Called(sql, vars)
return args.Get(0), args.Error(1)
}

func (mqa *MockQueryAgent) Close() {}
func (mqa *mockQueryAgent) Close() {}

type ArbitraryData struct {
type arbitraryData struct {
Num int
Text string
Boolean bool `db:"bool"`
Expand All @@ -36,7 +36,7 @@ var emptyResponse = []any{

func Test_parseQuery(t *testing.T) {
t.Run("Unmodified query", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test;"

Expand All @@ -47,7 +47,7 @@ func Test_parseQuery(t *testing.T) {
m.AssertCalled(t, "Query", query, nilMap)
})
t.Run("Query a slice parameter", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test WHERE id = $1;"

Expand All @@ -58,7 +58,7 @@ func Test_parseQuery(t *testing.T) {
m.AssertCalled(t, "Query", query, map[string]any{"1": 1})
})
t.Run("Query wuth zero value parameter", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test WHERE id = $1;"

Expand All @@ -69,7 +69,7 @@ func Test_parseQuery(t *testing.T) {
m.AssertCalled(t, "Query", query, map[string]any{"1": 0})
})
t.Run("Query with multiple slice parameters", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test WHERE id = $1 AND name = $2;"

Expand All @@ -80,7 +80,7 @@ func Test_parseQuery(t *testing.T) {
m.AssertCalled(t, "Query", query, map[string]any{"1": 1, "2": "test"})
})
t.Run("Query with map parameter", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test WHERE id = $id;"

Expand All @@ -91,18 +91,18 @@ func Test_parseQuery(t *testing.T) {
m.AssertCalled(t, "Query", query, map[string]any{"id": 1})
})
t.Run("Query with struct parameter", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test WHERE id = $num;"

m.On("Query", mock.Anything, mock.Anything).Return(emptyResponse, nil)
_, err := db.Exec(query, ArbitraryData{Num: 1})
_, err := db.Exec(query, arbitraryData{Num: 1})

assert.NoError(t, err)
m.AssertCalled(t, "Query", query, map[string]any{"num": 1, "bool": false, "text": ""})
})
t.Run("Query with multiple map parameters", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test WHERE id = $num AND name = $text;"

Expand All @@ -113,51 +113,51 @@ func Test_parseQuery(t *testing.T) {
m.AssertCalled(t, "Query", query, map[string]any{"num": 1, "text": "test"})
})
t.Run("Query with struct parameter and tag", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test WHERE id = $bool;"

m.On("Query", mock.Anything, mock.Anything).Return(emptyResponse, nil)
_, err := db.Exec(query, ArbitraryData{Boolean: true})
_, err := db.Exec(query, arbitraryData{Boolean: true})

assert.NoError(t, err)
m.AssertCalled(t, "Query", query, map[string]any{"bool": true, "num": 0, "text": ""})
})
t.Run("Query with multiple struct parameters", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test WHERE id = $num AND name = $text;"

m.On("Query", mock.Anything, mock.Anything).Return(emptyResponse, nil)
_, err := db.Exec(query, ArbitraryData{Num: 1}, ArbitraryData{Text: "test"})
_, err := db.Exec(query, arbitraryData{Num: 1}, arbitraryData{Text: "test"})

assert.NoError(t, err)
m.AssertCalled(t, "Query", query, map[string]any{"num": 0, "text": "test", "bool": false})
})
t.Run("Query with a mix of map and struct parameters", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test WHERE id = $num AND name = $text;"

m.On("Query", mock.Anything, mock.Anything).Return(emptyResponse, nil)
_, err := db.Exec(query, ArbitraryData{Text: "test"}, map[string]any{"num": 1})
_, err := db.Exec(query, arbitraryData{Text: "test"}, map[string]any{"num": 1})

assert.NoError(t, err)
m.AssertCalled(t, "Query", query, map[string]any{"num": 1, "text": "test", "bool": false})
})
t.Run("Query with a mix of slice and struct parameters", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test WHERE id = $1 AND name = $text;"

m.On("Query", mock.Anything, mock.Anything).Return(emptyResponse, nil)
_, err := db.Exec(query, 1, ArbitraryData{Text: "test"})
_, err := db.Exec(query, 1, arbitraryData{Text: "test"})

assert.NoError(t, err)
m.AssertCalled(t, "Query", query, map[string]any{"1": 1, "text": "test", "bool": false, "num": 0})
})
t.Run("Query with a mix of slice and map parameters", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test WHERE id = $1 AND name = $text;"

Expand All @@ -168,7 +168,7 @@ func Test_parseQuery(t *testing.T) {
m.AssertCalled(t, "Query", query, map[string]any{"1": 1, "text": "test"})
})
t.Run("Query with an id parameter", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test:$"

Expand All @@ -179,7 +179,7 @@ func Test_parseQuery(t *testing.T) {
m.AssertCalled(t, "Query", "SELECT * FROM test:1;", map[string]any{})
})
t.Run("Query with a string id parameter", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test:$"

Expand All @@ -190,7 +190,7 @@ func Test_parseQuery(t *testing.T) {
m.AssertCalled(t, "Query", "SELECT * FROM test:`1`;", map[string]any{})
})
t.Run("Query with multiple id parameters", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test:$, foo:$"

Expand All @@ -201,7 +201,7 @@ func Test_parseQuery(t *testing.T) {
m.AssertCalled(t, "Query", "SELECT * FROM test:1, foo:2;", map[string]any{})
})
t.Run("Query with an array id", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test:$"

Expand All @@ -212,7 +212,7 @@ func Test_parseQuery(t *testing.T) {
m.AssertCalled(t, "Query", "SELECT * FROM test:[1, '2'];", map[string]any{})
})
t.Run("Query with a range id", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test:$"

Expand All @@ -223,7 +223,7 @@ func Test_parseQuery(t *testing.T) {
m.AssertCalled(t, "Query", "SELECT * FROM test:[1, '2']..[3, '4'];", map[string]any{})
})
t.Run("Query with an id and slice parameter", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test:$ WHERE id = $1"

Expand All @@ -234,7 +234,7 @@ func Test_parseQuery(t *testing.T) {
m.AssertCalled(t, "Query", "SELECT * FROM test:`1` WHERE id = $1;", map[string]any{"1": 1})
})
t.Run("Query with an id, map and slice parameter", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test:$ WHERE name = $name AND id = $1"

Expand All @@ -248,7 +248,7 @@ func Test_parseQuery(t *testing.T) {
})
})
t.Run("Query with a datetime id", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test:$"

Expand All @@ -260,7 +260,7 @@ func Test_parseQuery(t *testing.T) {
m.AssertCalled(t, "Query", fmt.Sprintf("SELECT * FROM test:`%s`;", now.Format(time.RFC3339)), map[string]any{})
})
t.Run("Query with a ranged date id", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "SELECT * FROM test:$"

Expand All @@ -280,7 +280,7 @@ func Test_parseQuery(t *testing.T) {
)
})
t.Run("Query with a time and duration", func(t *testing.T) {
m := new(MockQueryAgent)
m := new(mockQueryAgent)
db := &DB{m}
query := "CREATE test SET time = $1 + $2"

Expand Down
32 changes: 27 additions & 5 deletions scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,23 +92,45 @@ func parseValue(srcVal reflect.Value, destVal reflect.Value) error {
}

if !srcVal.Type().AssignableTo(destVal.Type()) {
if destVal.Kind() == reflect.Int && srcVal.Kind() == reflect.Float64 {
if destVal.Type() == reflect.TypeOf(ID{}) && srcVal.Kind() == reflect.String {
destVal.Set(reflect.ValueOf(stringToID(strings.Split(srcVal.String(), ":")[1])))
return nil
} else if strings.Split(destVal.Type().String(), "[")[0] == "surgo.Record" {
if srcVal.Kind() == reflect.String {
destVal.FieldByName("ID").Set(reflect.ValueOf(stringToID(srcVal.String())))
return nil
} else {
underlyingType := destVal.FieldByName("Data").Elem().Type()
newInstance := reflect.New(underlyingType)
if err := scan(srcVal.Interface(), newInstance.Interface()); err != nil {
return err
}
destVal.FieldByName("Data").Set(newInstance.Elem())
return nil
}
} else if destVal.Kind() == reflect.Int && srcVal.Kind() == reflect.Float64 {
destVal.SetInt(int64(srcVal.Float()))
return nil
} else if destVal.Type() == reflect.TypeOf(time.Time{}) && srcVal.Kind() == reflect.String {
t, err := time.Parse(time.RFC3339, srcVal.String())
t, err := stringToTime(srcVal.String())
if err != nil {
return err
}

destVal.Set(reflect.ValueOf(t))
return nil
} else if srcVal.Kind() != destVal.Kind() {
destVal.Set(reflect.Zero(destVal.Type()))
} else if destVal.Type().String() == "time.Duration" && srcVal.Kind() == reflect.String {
d, err := stringToDuration(srcVal.String())
if err != nil {
return err
}

destVal.Set(reflect.ValueOf(d))
return nil
}

return fmt.Errorf("cannot assign %s to %s", srcVal.Type(), destVal.Type())
destVal.Set(reflect.Zero(destVal.Type()))
return nil
}

destVal.Set(srcVal)
Expand Down
Loading

0 comments on commit 56c40f1

Please sign in to comment.