goext/sq/converter.go

249 lines
7.8 KiB
Go
Raw Permalink Normal View History

2023-05-28 22:55:06 +02:00
package sq
import (
2024-01-05 10:25:05 +01:00
"encoding/json"
2023-05-28 22:55:06 +02:00
"errors"
"fmt"
2024-01-07 04:18:03 +01:00
"gogs.mikescher.com/BlackForestBytes/goext/exerr"
2023-05-28 22:55:06 +02:00
"gogs.mikescher.com/BlackForestBytes/goext/langext"
2023-12-29 19:25:36 +01:00
"gogs.mikescher.com/BlackForestBytes/goext/rfctime"
"reflect"
2024-02-21 18:32:06 +01:00
"strconv"
"strings"
2023-05-28 22:55:06 +02:00
"time"
)
type DBTypeConverter interface {
ModelTypeString() string
DBTypeString() string
ModelToDB(v any) (any, error)
DBToModel(v any) (any, error)
}
2024-01-14 17:06:42 +01:00
var ConverterBoolToBit = NewDBTypeConverter[bool, int64](func(v bool) (int64, error) {
return langext.Conditional(v, int64(1), int64(0)), nil
}, func(v int64) (bool, error) {
2023-05-28 22:55:06 +02:00
if v == 0 {
return false, nil
}
if v == 1 {
return true, nil
}
return false, errors.New(fmt.Sprintf("invalid valud for boolean: '%d'", v))
})
var ConverterTimeToUnixMillis = NewDBTypeConverter[time.Time, int64](func(v time.Time) (int64, error) {
return v.UnixMilli(), nil
}, func(v int64) (time.Time, error) {
return time.UnixMilli(v), nil
})
2023-12-29 19:25:36 +01:00
var ConverterRFCUnixMilliTimeToUnixMillis = NewDBTypeConverter[rfctime.UnixMilliTime, int64](func(v rfctime.UnixMilliTime) (int64, error) {
return v.UnixMilli(), nil
}, func(v int64) (rfctime.UnixMilliTime, error) {
return rfctime.NewUnixMilli(time.UnixMilli(v)), nil
})
var ConverterRFCUnixNanoTimeToUnixNanos = NewDBTypeConverter[rfctime.UnixNanoTime, int64](func(v rfctime.UnixNanoTime) (int64, error) {
return v.UnixNano(), nil
}, func(v int64) (rfctime.UnixNanoTime, error) {
return rfctime.NewUnixNano(time.Unix(0, v)), nil
})
var ConverterRFCUnixTimeToUnixSeconds = NewDBTypeConverter[rfctime.UnixTime, int64](func(v rfctime.UnixTime) (int64, error) {
return v.Unix(), nil
}, func(v int64) (rfctime.UnixTime, error) {
return rfctime.NewUnix(time.Unix(v, 0)), nil
})
2023-12-29 19:29:36 +01:00
// ConverterRFC339TimeToString
// Does not really use RFC339 - but sqlite does not understand timezones and the `T` delimiter
2023-12-29 19:25:36 +01:00
var ConverterRFC339TimeToString = NewDBTypeConverter[rfctime.RFC3339Time, string](func(v rfctime.RFC3339Time) (string, error) {
2023-12-29 19:29:36 +01:00
return v.Time().In(time.UTC).Format("2006-01-02 15:04:05"), nil
2023-12-29 19:25:36 +01:00
}, func(v string) (rfctime.RFC3339Time, error) {
2023-12-29 19:29:36 +01:00
t, err := time.Parse("2006-01-02 15:04:05", v)
2023-12-29 19:25:36 +01:00
if err != nil {
return rfctime.RFC3339Time{}, err
2023-05-28 22:55:06 +02:00
}
2023-12-29 19:25:36 +01:00
return rfctime.NewRFC3339(t), nil
})
2023-12-29 19:29:36 +01:00
// ConverterRFC339NanoTimeToString
// Does not really use RFC339 - but sqlite does not understand timezones and the `T` delimiter
2023-12-29 19:25:36 +01:00
var ConverterRFC339NanoTimeToString = NewDBTypeConverter[rfctime.RFC3339NanoTime, string](func(v rfctime.RFC3339NanoTime) (string, error) {
2023-12-29 19:29:36 +01:00
return v.Time().In(time.UTC).Format("2006-01-02 15:04:05.999999999"), nil
2023-12-29 19:25:36 +01:00
}, func(v string) (rfctime.RFC3339NanoTime, error) {
2023-12-29 19:29:36 +01:00
t, err := time.ParseInLocation("2006-01-02 15:04:05.999999999", v, time.UTC)
2023-12-29 19:25:36 +01:00
if err != nil {
return rfctime.RFC3339NanoTime{}, err
2023-05-28 22:55:06 +02:00
}
2023-12-29 19:25:36 +01:00
return rfctime.NewRFC3339Nano(t), nil
2023-05-28 22:55:06 +02:00
})
2024-02-21 18:32:06 +01:00
var ConverterRFCDateToString = NewDBTypeConverter[rfctime.Date, string](func(v rfctime.Date) (string, error) {
return fmt.Sprintf("%04d-%02d-%02d", v.Year, v.Month, v.Day), nil
}, func(v string) (rfctime.Date, error) {
split := strings.Split(v, "-")
if len(split) != 3 {
return rfctime.Date{}, errors.New("invalid date format: " + v)
}
year, err := strconv.ParseInt(split[0], 10, 32)
if err != nil {
return rfctime.Date{}, errors.New("invalid date format: " + v + ": " + err.Error())
}
month, err := strconv.ParseInt(split[0], 10, 32)
if err != nil {
return rfctime.Date{}, errors.New("invalid date format: " + v + ": " + err.Error())
}
day, err := strconv.ParseInt(split[0], 10, 32)
if err != nil {
return rfctime.Date{}, errors.New("invalid date format: " + v + ": " + err.Error())
}
return rfctime.Date{Year: int(year), Month: int(month), Day: int(day)}, nil
})
var ConverterRFCTimeToString = NewDBTypeConverter[rfctime.Time, string](func(v rfctime.Time) (string, error) {
return v.SerializeShort(), nil
}, func(v string) (rfctime.Time, error) {
res := rfctime.Time{}
err := res.Deserialize(v)
if err != nil {
return rfctime.Time{}, err
}
return res, nil
})
2024-01-05 10:25:05 +01:00
var ConverterJsonObjToString = NewDBTypeConverter[JsonObj, string](func(v JsonObj) (string, error) {
mrsh, err := json.Marshal(v)
if err != nil {
return "", err
}
return string(mrsh), nil
}, func(v string) (JsonObj, error) {
var mrsh JsonObj
if err := json.Unmarshal([]byte(v), &mrsh); err != nil {
return JsonObj{}, err
}
return mrsh, nil
})
var ConverterJsonArrToString = NewDBTypeConverter[JsonArr, string](func(v JsonArr) (string, error) {
mrsh, err := json.Marshal(v)
if err != nil {
return "", err
}
return string(mrsh), nil
}, func(v string) (JsonArr, error) {
var mrsh JsonArr
if err := json.Unmarshal([]byte(v), &mrsh); err != nil {
return JsonArr{}, err
}
return mrsh, nil
})
2024-01-07 04:18:03 +01:00
var ConverterExErrCategoryToString = NewDBTypeConverter[exerr.ErrorCategory, string](func(v exerr.ErrorCategory) (string, error) {
return v.Category, nil
}, func(v string) (exerr.ErrorCategory, error) {
for _, cat := range exerr.AllCategories {
if cat.Category == v {
return cat, nil
}
}
return exerr.CatUser, errors.New("failed to convert '" + v + "' to exerr.ErrorCategory")
})
var ConverterExErrSeverityToString = NewDBTypeConverter[exerr.ErrorSeverity, string](func(v exerr.ErrorSeverity) (string, error) {
return v.Severity, nil
}, func(v string) (exerr.ErrorSeverity, error) {
for _, sev := range exerr.AllSeverities {
if sev.Severity == v {
return sev, nil
}
}
return exerr.SevErr, errors.New("failed to convert '" + v + "' to exerr.ErrorSeverity")
})
var ConverterExErrTypeToString = NewDBTypeConverter[exerr.ErrorType, string](func(v exerr.ErrorType) (string, error) {
return v.Key, nil
}, func(v string) (exerr.ErrorType, error) {
for _, etp := range exerr.ListRegisteredTypes() {
if etp.Key == v {
return etp, nil
}
}
return exerr.NewType(v, nil), nil
})
2023-05-28 22:55:06 +02:00
type dbTypeConverterImpl[TModelData any, TDBData any] struct {
dbTypeString string
modelTypeString string
todb func(v TModelData) (TDBData, error)
tomodel func(v TDBData) (TModelData, error)
}
func (t *dbTypeConverterImpl[TModelData, TDBData]) ModelTypeString() string {
return t.modelTypeString
}
func (t *dbTypeConverterImpl[TModelData, TDBData]) DBTypeString() string {
return t.dbTypeString
}
func (t *dbTypeConverterImpl[TModelData, TDBData]) ModelToDB(v any) (any, error) {
if vv, ok := v.(TModelData); ok {
return t.todb(vv)
}
return nil, errors.New(fmt.Sprintf("Unexpected value in DBTypeConverter, expected '%s', found '%T'", t.modelTypeString, v))
}
func (t *dbTypeConverterImpl[TModelData, TDBData]) DBToModel(v any) (any, error) {
if vv, ok := v.(TDBData); ok {
return t.tomodel(vv)
}
return nil, errors.New(fmt.Sprintf("Unexpected value in DBTypeConverter, expected '%s', found '%T'", t.dbTypeString, v))
}
func NewDBTypeConverter[TModelData any, TDBData any](todb func(v TModelData) (TDBData, error), tomodel func(v TDBData) (TModelData, error)) DBTypeConverter {
return &dbTypeConverterImpl[TModelData, TDBData]{
dbTypeString: fmt.Sprintf("%T", *new(TDBData)),
modelTypeString: fmt.Sprintf("%T", *new(TModelData)),
todb: todb,
tomodel: tomodel,
}
}
2023-12-29 19:25:36 +01:00
func convertValueToDB(q Queryable, value any) (any, error) {
modelTypeStr := fmt.Sprintf("%T", value)
for _, conv := range q.ListConverter() {
if conv.ModelTypeString() == modelTypeStr {
return conv.ModelToDB(value)
}
}
if value != nil && reflect.TypeOf(value).Kind() == reflect.Ptr {
vof := reflect.ValueOf(value)
if vof.IsNil() {
return nil, nil
} else {
return convertValueToDB(q, vof.Elem().Interface())
}
}
return value, nil
}
func convertValueToModel(q Queryable, value any, destinationType string) (any, error) {
dbTypeString := fmt.Sprintf("%T", value)
for _, conv := range q.ListConverter() {
if conv.ModelTypeString() == destinationType && conv.DBTypeString() == dbTypeString {
return conv.DBToModel(value)
}
}
return value, nil
}