Skip to content

Commit

Permalink
Merge pull request #214 from laushunyu/master
Browse files Browse the repository at this point in the history
fix: failed valuer convert return unexpected true
  • Loading branch information
jinzhu authored Jul 18, 2024
2 parents 60a1fd2 + 71552f3 commit 659270e
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 9 deletions.
31 changes: 22 additions & 9 deletions copier.go
Original file line number Diff line number Diff line change
Expand Up @@ -610,9 +610,14 @@ func set(to, from reflect.Value, deepCopy bool, converters map[converterPair]Typ
}
}

// try convert directly
if from.Type().ConvertibleTo(to.Type()) {
to.Set(from.Convert(to.Type()))
} else if toScanner, ok := to.Addr().Interface().(sql.Scanner); ok {
return true, nil
}

// try Scanner
if toScanner, ok := to.Addr().Interface().(sql.Scanner); ok {
// `from` -> `to`
// *string -> sql.NullString
if from.Kind() == reflect.Ptr {
Expand All @@ -627,10 +632,13 @@ func set(to, from reflect.Value, deepCopy bool, converters map[converterPair]Typ
// string -> sql.NullString
// set `to` by invoking method Scan(`from`)
err := toScanner.Scan(from.Interface())
if err != nil {
return false, nil
if err == nil {
return true, nil
}
} else if fromValuer, ok := driverValuer(from); ok {
}

// try Valuer
if fromValuer, ok := driverValuer(from); ok {
// `from` -> `to`
// sql.NullString -> string
v, err := fromValuer.Value()
Expand All @@ -644,16 +652,21 @@ func set(to, from reflect.Value, deepCopy bool, converters map[converterPair]Typ
rv := reflect.ValueOf(v)
if rv.Type().AssignableTo(to.Type()) {
to.Set(rv)
} else if to.CanSet() && rv.Type().ConvertibleTo(to.Type()) {
return true, nil
}
if to.CanSet() && rv.Type().ConvertibleTo(to.Type()) {
to.Set(rv.Convert(to.Type()))
return true, nil
}
} else if from.Kind() == reflect.Ptr {
return set(to, from.Elem(), deepCopy, converters)
} else {
return false, nil
}

return true, nil
// from is ptr
if from.Kind() == reflect.Ptr {
return set(to, from.Elem(), deepCopy, converters)
}

return false, nil
}

// lookupAndCopyWithConverter looks up the type pair, on success the TypeConverter Fn func is called to copy src to dst field.
Expand Down
61 changes: 61 additions & 0 deletions copier_converter_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package copier_test

import (
"bytes"
"database/sql/driver"
"encoding/json"
"errors"
"reflect"
"strconv"
"testing"
"time"
Expand Down Expand Up @@ -218,3 +222,60 @@ func TestCopyWithConverterRaisingError(t *testing.T) {
return
}
}

type IntArray []int

func (a IntArray) Value() (driver.Value, error) {
return json.Marshal(a)
}

type Int int

type From struct {
Data IntArray
}

type To struct {
Data []byte
}

type FailedTo struct {
Data []Int
}

func TestValuerConv(t *testing.T) {
// when the field of struct implement driver.Valuer and cannot convert to dest type directly,
// copier.set() will return a unexpected (true, nil)

typ1 := reflect.TypeOf(IntArray{})
typ2 := reflect.TypeOf([]Int{})

if typ1 == typ2 || typ1.ConvertibleTo(typ2) || typ1.AssignableTo(typ2) {
// in 1.22 and older, u can not convert typ1 to typ2
t.Errorf("can not convert %v to %v direct", typ1, typ2)
}

var (
from = From{
Data: IntArray{1, 2, 3},
}
to To
failedTo FailedTo
)
if err := copier.Copy(&to, from); err != nil {
t.Fatal(err)
}
if err := copier.Copy(&failedTo, from); err != nil {
t.Fatal(err)
}

// Testcase1: valuer conv case
if !bytes.Equal(to.Data, []byte(`[1,2,3]`)) {
t.Errorf("can not convert %v to %v using valuer", typ1, typ2)
}

// Testcase2: fallback case when valuer conv failed
if len(failedTo.Data) != 3 || failedTo.Data[0] != 1 || failedTo.Data[1] != 2 || failedTo.Data[2] != 3 {
t.Errorf("copier failed from %#v to %#v", from, failedTo)
}
}

0 comments on commit 659270e

Please sign in to comment.