1
0
bind/scan.go
2025-08-31 17:11:09 +02:00

132 lines
2.5 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) {
if isAny(t) {
return v, true
}
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))
}