jinzhu / copier

Copier for golang, copy value from struct to struct and more
MIT License
5.58k stars 489 forks source link

Added registration for conversion function to different type #90

Open tomtwinkle opened 3 years ago

tomtwinkle commented 3 years ago

Changed so that you can add a function that can be converted to any type.

Can freely customize the type conversion by defining the function for TypeCopier by the user.


type TypePair struct {
    SrcType reflect.Type
    DstType reflect.Type
}

type TypedCopier interface {
    // Define the conversion process
    Copy(dstValue, srcValue reflect.Value) error
    // Defines the type of the source field and the type of the destination field
    Pairs() []TypePair
}

Example

type timeToStringCopier struct{}

func (t timeToStringCopier) Pairs() []copier.TypePair {
    return []copier.TypePair{
        {
            SrcType: reflect.TypeOf(time.Time{}),
            DstType: reflect.TypeOf(""),
        },
        {
            SrcType: reflect.TypeOf(&time.Time{}),
            DstType: reflect.TypeOf(""),
        },
    }
}

func (t timeToStringCopier) Copy(dst, src reflect.Value) error {
    const timeFormat = "2006-01-02T15:04:05.999999999Z07:00"
    if src.Kind() == reflect.Ptr && src.IsNil() {
        if dst.Kind() == reflect.Ptr {
            dst.Set(reflect.Zero(reflect.TypeOf("")))
        }
        return nil
    }

    var val string
    if src.Kind() == reflect.Ptr {
        s, ok := src.Interface().(*time.Time)
        if !ok {
            return errors.New("type not match")
        }
        val = s.Format(timeFormat)
    } else {
        s, ok := src.Interface().(time.Time)
        if !ok {
            return errors.New("type not match")
        }
        val = s.Format(timeFormat)
    }
    dst.Set(reflect.ValueOf(val))
    return nil
}

func main() {
    type SrcStruct1 struct {
        Field1 time.Time
        Field2 *time.Time
    }

    type DestStruct1 struct {
        Field1 string
        Field2 string
    }

    c := copier.NewCopier()
    c.Register(&timeToStringCopier{})

    testTime := time.Date(2021, 3, 5, 1, 30, 0, 123000000, time.UTC)
    src := SrcStruct1{
        Field1: testTime,
        Field2: &testTime,
    }
    var dst DestStruct1

    if err := c.Copy(&dst, src); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%#v", dst)
    // DestStruct1{Field1:"2021-03-05T01:30:00.123Z", Field2:"2021-03-05T01:30:00.123Z"}
}
jinzhu commented 3 years ago

Hello @tomtwinkle

Thank you for your PR, but maybe should pass those registered type in the Option struct? Also might be good to allow to register some default global converters.