137 lines
3.8 KiB
Go
137 lines
3.8 KiB
Go
package reflectext
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
var primitiveSerializer = map[reflect.Type]genSerializer{
|
|
|
|
reflect.TypeOf(""): newGenSerializer(serStringToString, serStringToString),
|
|
|
|
reflect.TypeOf(int(0)): newGenSerializer(serIntNumToString[int], serStringToSIntNum[int]),
|
|
reflect.TypeOf(int32(0)): newGenSerializer(serIntNumToString[int32], serStringToSIntNum[int32]),
|
|
reflect.TypeOf(int64(0)): newGenSerializer(serIntNumToString[int64], serStringToSIntNum[int64]),
|
|
|
|
reflect.TypeOf(uint(0)): newGenSerializer(serIntNumToString[uint], serStringToUIntNum[uint]),
|
|
reflect.TypeOf(uint32(0)): newGenSerializer(serIntNumToString[uint32], serStringToUIntNum[uint32]),
|
|
reflect.TypeOf(uint64(0)): newGenSerializer(serIntNumToString[uint64], serStringToUIntNum[uint64]),
|
|
|
|
reflect.TypeOf(float32(0)): newGenSerializer(serFloatNumToString[float32], serStringToFloatNum[float32]),
|
|
reflect.TypeOf(float64(0)): newGenSerializer(serFloatNumToString[float64], serStringToFloatNum[float64]),
|
|
|
|
reflect.TypeOf(true): newGenSerializer(serBoolToString, serStringToBool),
|
|
|
|
reflect.TypeOf(primitive.ObjectID{}): newGenSerializer(serObjectIDToString, serStringToObjectID),
|
|
|
|
reflect.TypeOf(time.Time{}): newGenSerializer(serTimeToString, serStringToTime),
|
|
}
|
|
|
|
type genSerializer struct {
|
|
ToString func(v any) (string, error)
|
|
FromString func(v string) (any, error)
|
|
}
|
|
|
|
func newGenSerializer[TData any](tostr func(v TData) (string, error), fromstr func(v string) (TData, error)) genSerializer {
|
|
return genSerializer{
|
|
ToString: func(v any) (string, error) {
|
|
if tdv, ok := v.(TData); ok {
|
|
rv, err := tostr(tdv)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return rv, nil
|
|
} else {
|
|
return "", errors.New(fmt.Sprintf("cannot convert type %T to TData (%T)", v, *new(TData)))
|
|
}
|
|
},
|
|
FromString: func(v string) (any, error) {
|
|
nv, err := fromstr(v)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return nv, nil
|
|
},
|
|
}
|
|
}
|
|
|
|
func serStringToString(v string) (string, error) {
|
|
return v, nil
|
|
}
|
|
|
|
func serIntNumToString[TNum langext.IntegerConstraint](v TNum) (string, error) {
|
|
return strconv.FormatInt(int64(v), 10), nil
|
|
}
|
|
|
|
func serStringToSIntNum[TNum langext.SignedConstraint](v string) (TNum, error) {
|
|
r, err := strconv.ParseInt(v, 10, 64)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return TNum(r), nil
|
|
}
|
|
|
|
func serStringToUIntNum[TNum langext.UnsignedConstraint](v string) (TNum, error) {
|
|
r, err := strconv.ParseUint(v, 10, 64)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return TNum(r), nil
|
|
}
|
|
|
|
func serFloatNumToString[TNum langext.FloatConstraint](v TNum) (string, error) {
|
|
return strconv.FormatFloat(float64(v), 'f', -1, 64), nil
|
|
}
|
|
|
|
func serStringToFloatNum[TNum langext.FloatConstraint](v string) (TNum, error) {
|
|
r, err := strconv.ParseFloat(v, 64)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return TNum(r), nil
|
|
}
|
|
|
|
func serBoolToString(v bool) (string, error) {
|
|
return langext.Conditional(v, "true", "false"), nil
|
|
}
|
|
|
|
func serStringToBool(v string) (bool, error) {
|
|
if strings.ToLower(v) == "true" {
|
|
return true, nil
|
|
}
|
|
if strings.ToLower(v) == "false" {
|
|
return true, nil
|
|
}
|
|
return false, errors.New(fmt.Sprintf("invalid boolean value '%s'", v))
|
|
}
|
|
|
|
func serObjectIDToString(v primitive.ObjectID) (string, error) {
|
|
return v.Hex(), nil
|
|
}
|
|
|
|
func serStringToObjectID(v string) (primitive.ObjectID, error) {
|
|
if rv, err := primitive.ObjectIDFromHex(v); err == nil {
|
|
return rv, nil
|
|
} else {
|
|
return primitive.ObjectID{}, err
|
|
}
|
|
}
|
|
|
|
func serTimeToString(v time.Time) (string, error) {
|
|
return v.Format(time.RFC3339Nano), nil
|
|
}
|
|
|
|
func serStringToTime(v string) (time.Time, error) {
|
|
if rv, err := time.Parse(time.RFC3339Nano, v); err == nil {
|
|
return rv, nil
|
|
} else {
|
|
return time.Time{}, err
|
|
}
|
|
}
|