forked from issue9/conv
-
Notifications
You must be signed in to change notification settings - Fork 0
/
value.go
132 lines (114 loc) · 2.96 KB
/
value.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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// SPDX-License-Identifier: MIT
package conv
import (
"errors"
"fmt"
"reflect"
)
// Value 将 source 的值保存到成 target 中。
//
// 如果 source 为 nil,则会将 target 的值设置为其默认的零值。
//
// 若类型不能直接转换,会尝试其它种方式转换,比如 strconv.ParseInt() 等。
func Value(source interface{}, target reflect.Value) error {
kind := target.Kind()
for kind == reflect.Ptr {
target = target.Elem()
kind = target.Kind()
}
if !target.CanSet() {
return fmt.Errorf("无法改变 target 的值[%v]", target.Kind())
}
if !target.IsValid() {
return errors.New("无效的 target 值")
}
if source == nil {
target.Set(reflect.Zero(target.Type()))
return nil
}
switch kind {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
val, err := Uint64(source)
if err != nil {
return err
}
target.SetUint(val)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
val, err := Int64(source)
if err != nil {
return err
}
target.SetInt(val)
case reflect.Float32, reflect.Float64:
val, err := Float64(source)
if err != nil {
return err
}
target.SetFloat(val)
case reflect.Bool:
val, err := Bool(source)
if err != nil {
return err
}
target.SetBool(val)
case reflect.String:
val, err := String(source)
if err != nil {
return err
}
target.SetString(val)
case reflect.Slice:
s := reflect.ValueOf(source)
sk := s.Kind()
tek := target.Type().Elem().Kind()
// []byte []rune 和 string 之间可以互相转换
if sk == reflect.String && (tek == reflect.Uint8 || tek == reflect.Int32) {
return valueDefault(source, target)
}
if sk != reflect.Array && sk != reflect.Slice {
return typeError(source, "slice")
}
l := s.Len()
tmp := reflect.MakeSlice(target.Type(), l, l)
for i := 0; i < l; i++ {
si := s.Index(i).Interface()
if err := Value(si, tmp.Index(i)); err != nil {
return err
}
}
target.Set(tmp)
case reflect.Array:
s := reflect.ValueOf(source)
sk := s.Kind()
tek := target.Type().Elem().Kind()
// []byte []rune 和 string 之间可以互相转换
if sk == reflect.String && (tek == reflect.Uint8 || tek == reflect.Int32) {
return valueDefault(source, target)
}
if sk != reflect.Array && sk != reflect.Slice {
return typeError(source, "array")
}
l := s.Len()
if l != target.Len() {
return fmt.Errorf("两者长度不一样,无法转换 %d: %d", l, target.Len())
}
for i := 0; i < l; i++ {
si := s.Index(i).Interface()
if err := Value(si, target.Index(i)); err != nil {
return err
}
}
default:
return valueDefault(source, target)
}
return nil
}
func valueDefault(source interface{}, target reflect.Value) error {
sourceValue := reflect.ValueOf(source)
targetType := target.Type()
if !sourceValue.Type().ConvertibleTo(targetType) {
return typeError(source, targetType.String())
}
target.Set(sourceValue.Convert(targetType))
return nil
}