package bind import ( "errors" "fmt" "math" "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 floatToTime(f float64) time.Time { s := int64(f) ns := int64((f - float64(s)) * 1_000_000_000) return time.Unix(s, ns) } func scanConvert(t reflect.Type, v any) (any, bool) { if isAny(t) { return v, true } r := reflect.ValueOf(v) if r.CanConvert(t) { return r.Convert(t).Interface(), true } if t != reflect.TypeFor[time.Time]() { return nil, false } fi := reflect.TypeFor[float64]() if !r.CanConvert(fi) { return nil, false } f := r.Convert(fi).Interface().(float64) return floatToTime(f), true } func parseTime(s string) (any, error) { const errMsg = "failed to parse time" for _, l := range timeLayouts { if t, err := time.Parse(l, s); err == nil { return t, nil } } ss := strings.Split(s, ".") if len(ss) > 2 { return nil, errors.New(errMsg) } if len(ss) == 1 { i, err := parseInt(ss[0], 8) if err != nil { return nil, errors.New(errMsg) } return time.Unix(i, 0), nil } sec, err := parseInt(ss[0], 8) if err != nil { return nil, errors.New(errMsg) } dec := strings.TrimLeft(ss[1], "0") i, err := parseInt(dec, 8) if err != nil { return nil, errors.New(errMsg) } zeros := len(ss[1]) - len(dec) nanosec := int64(float64(i) * math.Pow10(9-zeros-len(dec))) return time.Unix(sec, nanosec), nil } 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 !isDuration(t) || 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) { 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)) }