128 lines
2.4 KiB
Go
128 lines
2.4 KiB
Go
package bind
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
var timeLayouts = []string{
|
|
time.RFC3339,
|
|
time.RFC3339Nano,
|
|
time.DateTime,
|
|
time.DateOnly,
|
|
time.TimeOnly,
|
|
time.RFC822,
|
|
time.RFC822Z,
|
|
time.RFC850,
|
|
time.RFC1123,
|
|
time.RFC1123Z,
|
|
time.ANSIC,
|
|
time.UnixDate,
|
|
time.RubyDate,
|
|
time.Kitchen,
|
|
time.Layout,
|
|
time.Stamp,
|
|
time.StampMilli,
|
|
time.StampMicro,
|
|
time.StampNano,
|
|
}
|
|
|
|
func intParse[T any](parse func(string, int, int) (T, error), s string, byteSize int) (T, error) {
|
|
bitSize := byteSize * 8
|
|
switch {
|
|
case strings.HasPrefix(s, "0b"):
|
|
return parse(s[2:], 2, bitSize)
|
|
case strings.HasPrefix(s, "0x"):
|
|
return parse(s[2:], 16, bitSize)
|
|
case strings.HasPrefix(s, "0"):
|
|
return parse(s[1:], 8, bitSize)
|
|
default:
|
|
return parse(s, 10, bitSize)
|
|
}
|
|
}
|
|
|
|
func parseInt(s string, byteSize int) (int64, error) {
|
|
return intParse(strconv.ParseInt, s, byteSize)
|
|
}
|
|
|
|
func parseUint(s string, byteSize int) (uint64, error) {
|
|
return intParse(strconv.ParseUint, s, byteSize)
|
|
}
|
|
|
|
func scanConvert(t reflect.Type, v any) (any, bool) {
|
|
r := reflect.ValueOf(v)
|
|
if !r.CanConvert(t) {
|
|
return nil, false
|
|
}
|
|
|
|
return r.Convert(t).Interface(), true
|
|
}
|
|
|
|
func parseTime(s string) (any, error) {
|
|
for _, l := range timeLayouts {
|
|
if t, err := time.Parse(l, s); err == nil {
|
|
return t, nil
|
|
}
|
|
}
|
|
|
|
return time.Time{}, errors.New("failed to parse time")
|
|
}
|
|
|
|
func scanString(t reflect.Type, s string) (any, bool) {
|
|
var (
|
|
v any
|
|
err error
|
|
)
|
|
|
|
switch t.Kind() {
|
|
case reflect.Bool:
|
|
v, err = strconv.ParseBool(s)
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
if isDuration(t) {
|
|
v, err = time.ParseDuration(s)
|
|
}
|
|
|
|
if err != nil {
|
|
v, err = parseInt(s, int(t.Size()))
|
|
}
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
v, err = parseUint(s, int(t.Size()))
|
|
case reflect.Float32, reflect.Float64:
|
|
v, err = strconv.ParseFloat(s, int(t.Size())*8)
|
|
case reflect.String:
|
|
v = s
|
|
default:
|
|
if isTime(t) {
|
|
v, err = parseTime(s)
|
|
} else {
|
|
return nil, false
|
|
}
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, false
|
|
}
|
|
|
|
p := reflect.New(t)
|
|
p.Elem().Set(reflect.ValueOf(v).Convert(t))
|
|
return p.Elem().Interface(), true
|
|
}
|
|
|
|
func scan(t reflect.Type, v any) (any, bool) {
|
|
// time and duration support
|
|
if vv, ok := scanConvert(t, v); ok {
|
|
return vv, true
|
|
}
|
|
|
|
r := reflect.ValueOf(v)
|
|
if r.Kind() != reflect.String {
|
|
return nil, false
|
|
}
|
|
|
|
return scanString(t, fmt.Sprint(v))
|
|
}
|