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 } }