forked from mattn/anko
-
Notifications
You must be signed in to change notification settings - Fork 0
/
toX.go
171 lines (153 loc) · 3.6 KB
/
toX.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
package core
import (
"fmt"
"reflect"
"strconv"
"strings"
"time"
"github.com/mattn/anko/env"
)
// ImportToX adds all the toX to the env given
func ImportToX(e *env.Env) {
e.Define("toBool", func(v interface{}) bool {
rv := reflect.ValueOf(v)
if !rv.IsValid() {
return false
}
nt := reflect.TypeOf(true)
if rv.Type().ConvertibleTo(nt) {
return rv.Convert(nt).Bool()
}
if rv.Type().ConvertibleTo(reflect.TypeOf(1.0)) && rv.Convert(reflect.TypeOf(1.0)).Float() > 0.0 {
return true
}
if rv.Kind() == reflect.String {
s := strings.ToLower(v.(string))
if s == "y" || s == "yes" {
return true
}
b, err := strconv.ParseBool(s)
if err == nil {
return b
}
}
return false
})
e.Define("toString", func(v interface{}) string {
if b, ok := v.([]byte); ok {
return string(b)
}
return fmt.Sprint(v)
})
e.Define("toInt", func(v interface{}) int64 {
rv := reflect.ValueOf(v)
if !rv.IsValid() {
return 0
}
nt := reflect.TypeOf(1)
if rv.Type().ConvertibleTo(nt) {
return rv.Convert(nt).Int()
}
if rv.Kind() == reflect.String {
i, err := strconv.ParseInt(v.(string), 10, 64)
if err == nil {
return i
}
f, err := strconv.ParseFloat(v.(string), 64)
if err == nil {
return int64(f)
}
}
if rv.Kind() == reflect.Bool {
if v.(bool) {
return 1
}
}
return 0
})
e.Define("toFloat", func(v interface{}) float64 {
rv := reflect.ValueOf(v)
if !rv.IsValid() {
return 0
}
nt := reflect.TypeOf(1.0)
if rv.Type().ConvertibleTo(nt) {
return rv.Convert(nt).Float()
}
if rv.Kind() == reflect.String {
f, err := strconv.ParseFloat(v.(string), 64)
if err == nil {
return f
}
}
if rv.Kind() == reflect.Bool {
if v.(bool) {
return 1.0
}
}
return 0.0
})
e.Define("toChar", func(s rune) string {
return string(s)
})
e.Define("toRune", func(s string) rune {
if len(s) == 0 {
return 0
}
return []rune(s)[0]
})
e.Define("toBoolSlice", func(v []interface{}) []bool {
var result []bool
toSlice(v, &result)
return result
})
e.Define("toStringSlice", func(v []interface{}) []string {
var result []string
toSlice(v, &result)
return result
})
e.Define("toIntSlice", func(v []interface{}) []int64 {
var result []int64
toSlice(v, &result)
return result
})
e.Define("toFloatSlice", func(v []interface{}) []float64 {
var result []float64
toSlice(v, &result)
return result
})
e.Define("toByteSlice", func(s string) []byte {
return []byte(s)
})
e.Define("toRuneSlice", func(s string) []rune {
return []rune(s)
})
e.Define("toDuration", func(v int64) time.Duration {
return time.Duration(v)
})
}
// toSlice takes in a "generic" slice and converts and copies
// it's elements into the typed slice pointed at by ptr.
// Note that this is a costly operation.
func toSlice(from []interface{}, ptr interface{}) {
// Value of the pointer to the target
obj := reflect.Indirect(reflect.ValueOf(ptr))
// We can't just convert from interface{} to whatever the target is (diff memory layout),
// so we need to create a New slice of the proper type and copy the values individually
t := reflect.TypeOf(ptr).Elem()
tt := t.Elem()
slice := reflect.MakeSlice(t, len(from), len(from))
// Copying the data, val is an addressable Pointer of the actual target type
val := reflect.Indirect(reflect.New(tt))
for i := 0; i < len(from); i++ {
v := reflect.ValueOf(from[i])
if v.IsValid() && v.Type().ConvertibleTo(tt) {
val.Set(v.Convert(tt))
} else {
val.Set(reflect.Zero(tt))
}
slice.Index(i).Set(val)
}
// Ok now assign our slice to the target pointer
obj.Set(slice)
}