package sq import ( "errors" "fmt" "gogs.mikescher.com/BlackForestBytes/goext/langext" "reflect" ) type DBTypeConverter interface { ModelTypeString() string DBTypeString() string ModelToDB(v any) (any, error) DBToModel(v any) (any, error) } type DBDataConstraint interface { string | langext.NumberConstraint | []byte } type DatabaseConvertible[TModelData any, TDBData DBDataConstraint] interface { MarshalToDB(v TModelData) (TDBData, error) UnmarshalToModel(v TDBData) (TModelData, error) } type dbTypeConverterImpl[TModelData any, TDBData DBDataConstraint] 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 DBDataConstraint](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, } } func NewAutoDBTypeConverter[TDBData DBDataConstraint, TModelData DatabaseConvertible[TModelData, TDBData]](obj TModelData) DBTypeConverter { return &dbTypeConverterImpl[TModelData, TDBData]{ dbTypeString: fmt.Sprintf("%T", *new(TDBData)), modelTypeString: fmt.Sprintf("%T", *new(TModelData)), todb: obj.MarshalToDB, tomodel: obj.UnmarshalToModel, } } 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 }