-
Notifications
You must be signed in to change notification settings - Fork 0
/
cpy_is_zero.go
106 lines (95 loc) · 2.92 KB
/
cpy_is_zero.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// Package cpy
package cpy
import (
"database/sql"
"fmt"
"reflect"
runtimeDebug "runtime/debug"
)
// CopyToIsZero Копирование значений только если значение в объекте назначения пустое.
func (cpy *impl) CopyToIsZero(toRv reflect.Value, fromRv reflect.Value) (err error) {
var (
to reflect.Value
from reflect.Value
fromT reflect.Type
fromV reflect.Value
field reflect.StructField
fieldName string
)
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("catch panic: %v\ncall stack is:\n%s", e, string(runtimeDebug.Stack()))
}
}()
to, from, fromT = cpy.Indirect(toRv), cpy.Indirect(fromRv), cpy.IndirectType(fromRv.Type())
for _, field = range cpy.Fields(fromT) {
fieldName = field.Name
if fromV = from.FieldByName(fieldName); fromV.IsValid() && !fromV.IsZero() {
err = cpy.SetToFieldIsZero(to, fieldName, fromV)
}
}
return
}
// SetToFieldIsZero Копирование одного поля по имени, только если значение в объекте назначения пустое.
func (cpy *impl) SetToFieldIsZero(toRv reflect.Value, toName string, fromV reflect.Value) (err error) {
var (
toV, toM reflect.Value
values []reflect.Value
ok bool
)
switch toV = toRv.FieldByName(toName); toV.IsValid() {
case true:
if toV.CanSet() {
if ok, err = cpy.SetToIsZero(toV, fromV); !ok {
if fromV.Kind() == reflect.Func && fromV.Type().NumIn() == 0 && fromV.Type().NumOut() >= 1 {
if values = fromV.Call([]reflect.Value{}); len(values) > 0 {
if ok, err = cpy.SetToIsZero(toV, values[0]); err != nil {
return
}
}
} else {
err = cpy.CopyToIsZero(toV.Addr(), fromV)
}
}
}
default:
if toM = toRv.MethodByName(toName); !toM.IsValid() && toRv.CanAddr() {
toM = toRv.Addr().MethodByName(toName)
}
if toM.IsValid() && toM.Type().NumIn() == 1 && fromV.Type().AssignableTo(toM.Type().In(0)) {
toM.Call([]reflect.Value{fromV})
}
}
return
}
// SetToIsZero Установка значения только если значение получателя является пустым значением.
func (cpy *impl) SetToIsZero(to reflect.Value, from reflect.Value) (ok bool, err error) {
var scanner sql.Scanner
if !from.IsValid() {
return
}
if to.Kind() == reflect.Ptr {
if to.IsNil() {
to.Set(reflect.New(to.Type().Elem()))
}
to, ok = to.Elem(), true
}
// Пропускаем поля с установленными не пустыми значениями
if !to.IsZero() {
return
}
switch {
case from.Type().ConvertibleTo(to.Type()):
to.Set(from.Convert(to.Type()))
ok = true
case from.Kind() == reflect.Ptr:
ok, err = cpy.Set(to, from.Elem())
default:
if scanner, ok = to.Addr().Interface().(sql.Scanner); ok {
if err, ok = scanner.Scan(from.Interface()), false; err == nil {
ok = true
}
}
}
return
}