package record import ( "fmt" "reflect" "strconv" ) // Record stores mixed scalar data (Boolean, numeric, and string) and provides type-safe methods to retrieve it. // It automatically casts values where possible. // // https://go.dev/ref/spec#Types type Record map[string]any func (r Record) Bool(column string) bool { value, _ := r.BoolSafe(column) return value } func (r Record) BoolSafe(column string) (bool, bool) { if t, ok := r.TypeOf(column); ok { switch t.Name() { case "bool": return r[column].(bool), true case "int": return r[column].(int) > 0, true case "int16": return r[column].(int16) > 0, true case "int32": return r[column].(int32) > 0, true case "int64": return r[column].(int64) > 0, true case "string": if value, err := strconv.ParseBool(r[column].(string)); err == nil { return value, true } case "uint": return r[column].(uint) > 0, true case "uint8": return r[column].(uint8) > 0, true case "uint16": return r[column].(uint16) > 0, true case "uint32": return r[column].(uint32) > 0, true case "uint64": return r[column].(uint64) > 0, true } } return false, false } func (r Record) Byte(column string) byte { value, _ := r.ByteSafe(column) return value } func (r Record) ByteSafe(column string) (byte, bool) { if t, ok := r.TypeOf(column); ok { switch t.Name() { case "byte": return r[column].(byte), true case "uint8": return byte(r[column].(uint8)), true } } return byte(0), false } func (r Record) Float32(column string) float32 { value, _ := r.Float32Safe(column) return value } func (r Record) Float32Safe(column string) (float32, bool) { if t, ok := r.TypeOf(column); ok { switch t.Name() { case "float32": return r[column].(float32), true case "float64": return float32(r[column].(float64)), true case "int": return float32(r[column].(int)), true case "int16": return float32(r[column].(int16)), true case "int32": return float32(r[column].(int32)), true case "int64": return float32(r[column].(int64)), true case "string": if value, err := strconv.ParseFloat(r[column].(string), 32); err == nil { return float32(value), true } case "uint": return float32(r[column].(uint)), true case "uint8": return float32(r[column].(uint8)), true case "uint16": return float32(r[column].(uint16)), true case "uint32": return float32(r[column].(uint32)), true case "uint64": return float32(r[column].(uint64)), true } } return 0, false } func (r Record) Float64(column string) float64 { value, _ := r.Float64Safe(column) return value } func (r Record) Float64Safe(column string) (float64, bool) { if t, ok := r.TypeOf(column); ok { switch t.Name() { case "float32": return float64(r[column].(float32)), true case "float64": return r[column].(float64), true case "int": return float64(r[column].(int)), true case "int16": return float64(r[column].(int16)), true case "int32": return float64(r[column].(int32)), true case "int64": return float64(r[column].(int64)), true case "string": if value, err := strconv.ParseFloat(r[column].(string), 64); err == nil { return float64(value), true } case "uint": return float64(r[column].(uint)), true case "uint8": return float64(r[column].(uint8)), true case "uint16": return float64(r[column].(uint16)), true case "uint32": return float64(r[column].(uint32)), true case "uint64": return float64(r[column].(uint64)), true } } return 0, false } func (r Record) Has(column string) bool { _, ok := r.TypeOf(column) return ok } func (r Record) Int(column string) int { value, _ := r.IntSafe(column) return value } func (r Record) IntSafe(column string) (int, bool) { if t, ok := r.TypeOf(column); ok { switch t.Name() { case "bool": if r[column].(bool) { return 1, true } else { return 0, true } case "int": return r[column].(int), true case "int16": return int(r[column].(int16)), true case "int32": return int(r[column].(int32)), true case "int64": return int(r[column].(int64)), true case "string": if value, err := strconv.ParseInt(r[column].(string), 10, 0); err == nil { return int(value), true } case "uint": return int(r[column].(uint)), true case "uint8": return int(r[column].(uint8)), true case "uint16": return int(r[column].(uint16)), true case "uint32": return int(r[column].(uint32)), true case "uint64": return int(r[column].(uint64)), true } } return 0, false } func (r Record) Int16(column string) int16 { value, _ := r.Int16Safe(column) return value } func (r Record) Int16Safe(column string) (int16, bool) { if t, ok := r.TypeOf(column); ok { switch t.Name() { case "bool": if r[column].(bool) { return 1, true } else { return 0, true } case "int": return int16(r[column].(int)), true case "int16": return r[column].(int16), true case "int32": return int16(r[column].(int32)), true case "int64": return int16(r[column].(int64)), true case "string": if value, err := strconv.ParseInt(r[column].(string), 10, 16); err == nil { return int16(value), true } case "uint": return int16(r[column].(uint)), true case "uint8": return int16(r[column].(uint8)), true case "uint16": return int16(r[column].(uint16)), true case "uint32": return int16(r[column].(uint32)), true case "uint64": return int16(r[column].(uint64)), true } } return 0, false } func (r Record) Int32(column string) int32 { value, _ := r.Int32Safe(column) return value } func (r Record) Int32Safe(column string) (int32, bool) { if t, ok := r.TypeOf(column); ok { switch t.Name() { case "bool": if r[column].(bool) { return 1, true } else { return 0, true } case "int": return int32(r[column].(int)), true case "int16": return int32(r[column].(int16)), true case "int32": return r[column].(int32), true case "int64": return int32(r[column].(int64)), true case "string": if value, err := strconv.ParseInt(r[column].(string), 10, 32); err == nil { return int32(value), true } case "uint": return int32(r[column].(uint)), true case "uint8": return int32(r[column].(uint8)), true case "uint16": return int32(r[column].(uint16)), true case "uint32": return int32(r[column].(uint32)), true case "uint64": return int32(r[column].(uint64)), true } } return 0, false } func (r Record) Int64(column string) int64 { value, _ := r.Int64Safe(column) return value } func (r Record) Int64Safe(column string) (int64, bool) { if t, ok := r.TypeOf(column); ok { switch t.Name() { case "bool": if r[column].(bool) { return 1, true } else { return 0, true } case "int": return int64(r[column].(int)), true case "int16": return int64(r[column].(int16)), true case "int32": return int64(r[column].(int32)), true case "int64": return r[column].(int64), true case "string": if value, err := strconv.ParseInt(r[column].(string), 10, 64); err == nil { return value, true } case "uint": return int64(r[column].(uint)), true case "uint8": return int64(r[column].(uint8)), true case "uint16": return int64(r[column].(uint16)), true case "uint32": return int64(r[column].(uint32)), true case "uint64": return int64(r[column].(uint64)), true } } return 0, false } func (r Record) String(column string) string { value, _ := r.StringSafe(column) return value } func (r Record) StringSafe(column string) (string, bool) { if t, ok := r.TypeOf(column); ok { switch t.Name() { case "string": return r[column].(string), true default: return fmt.Sprint(r[column]), true } } return "", false } func (r Record) TypeOf(column string) (reflect.Type, bool) { if r[column] != nil { return reflect.TypeOf(r[column]), true } return nil, false } func (r Record) Uint(column string) uint { value, _ := r.UintSafe(column) return value } func (r Record) UintSafe(column string) (uint, bool) { if t, ok := r.TypeOf(column); ok { switch t.Name() { case "bool": if r[column].(bool) { return 1, true } else { return 0, true } case "int": return uint(r[column].(int)), true case "int16": return uint(r[column].(int16)), true case "int32": return uint(r[column].(int32)), true case "int64": return uint(r[column].(int64)), true case "string": if value, err := strconv.ParseUint(r[column].(string), 10, 0); err == nil { return uint(value), true } case "uint": return r[column].(uint), true case "uint8": return uint(r[column].(uint8)), true case "uint16": return uint(r[column].(uint16)), true case "uint32": return uint(r[column].(uint32)), true case "uint64": return uint(r[column].(uint64)), true } } return 0, false } func (r Record) Uint8(column string) uint8 { value, _ := r.Uint8Safe(column) return value } func (r Record) Uint8Safe(column string) (uint8, bool) { if t, ok := r.TypeOf(column); ok { switch t.Name() { case "bool": if r[column].(bool) { return 1, true } else { return 0, true } case "int": return uint8(r[column].(int)), true case "int16": return uint8(r[column].(int16)), true case "int32": return uint8(r[column].(int32)), true case "int64": return uint8(r[column].(int64)), true case "string": if value, err := strconv.ParseUint(r[column].(string), 10, 16); err == nil { return uint8(value), true } case "uint": return uint8(r[column].(uint)), true case "uint8": return r[column].(uint8), true case "uint16": return uint8(r[column].(uint16)), true case "uint32": return uint8(r[column].(uint32)), true case "uint64": return uint8(r[column].(uint64)), true } } return 0, false } func (r Record) Uint16(column string) uint16 { value, _ := r.Uint16Safe(column) return value } func (r Record) Uint16Safe(column string) (uint16, bool) { if t, ok := r.TypeOf(column); ok { switch t.Name() { case "bool": if r[column].(bool) { return 1, true } else { return 0, true } case "int": return uint16(r[column].(int)), true case "int16": return uint16(r[column].(int16)), true case "int32": return uint16(r[column].(int32)), true case "int64": return uint16(r[column].(int64)), true case "string": if value, err := strconv.ParseUint(r[column].(string), 10, 16); err == nil { return uint16(value), true } case "uint": return uint16(r[column].(uint)), true case "uint8": return uint16(r[column].(uint8)), true case "uint16": return r[column].(uint16), true case "uint32": return uint16(r[column].(uint32)), true case "uint64": return uint16(r[column].(uint64)), true } } return 0, false } func (r Record) Uint32(column string) uint32 { value, _ := r.Uint32Safe(column) return value } func (r Record) Uint32Safe(column string) (uint32, bool) { if t, ok := r.TypeOf(column); ok { switch t.Name() { case "bool": if r[column].(bool) { return 1, true } else { return 0, true } case "int": return uint32(r[column].(int)), true case "int16": return uint32(r[column].(int16)), true case "int32": return uint32(r[column].(int32)), true case "int64": return uint32(r[column].(int64)), true case "string": if value, err := strconv.ParseUint(r[column].(string), 10, 32); err == nil { return uint32(value), true } case "uint": return uint32(r[column].(uint)), true case "uint8": return uint32(r[column].(uint8)), true case "uint16": return uint32(r[column].(uint16)), true case "uint32": return r[column].(uint32), true case "uint64": return uint32(r[column].(uint64)), true } } return 0, false } func (r Record) Uint64(column string) uint64 { value, _ := r.Uint64Safe(column) return value } func (r Record) Uint64Safe(column string) (uint64, bool) { if t, ok := r.TypeOf(column); ok { switch t.Name() { case "bool": if r[column].(bool) { return 1, true } else { return 0, true } case "int": return uint64(r[column].(int)), true case "int16": return uint64(r[column].(int16)), true case "int32": return uint64(r[column].(int32)), true case "int64": return uint64(r[column].(int64)), true case "string": if value, err := strconv.ParseUint(r[column].(string), 10, 64); err == nil { return value, true } case "uint": return uint64(r[column].(uint)), true case "uint8": return uint64(r[column].(uint8)), true case "uint16": return uint64(r[column].(uint16)), true case "uint32": return uint64(r[column].(uint32)), true case "uint64": return r[column].(uint64), true } } return 0, false }