2023-06-07 16:58:17 +02:00
|
|
|
package reflectext
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
|
|
|
"reflect"
|
|
|
|
)
|
|
|
|
|
|
|
|
// PrimitiveStringSerializer is used to serialize primitive types (and a few more) from and to string
|
|
|
|
// This is not really intended to be user facing, and more as a simple building block for other mechanisms
|
|
|
|
// supports:
|
|
|
|
// - golang primitives (ints, uints, floats, bool, string)
|
|
|
|
// - type aliases
|
|
|
|
// - time.Time
|
|
|
|
// - primitive.ObjectID
|
|
|
|
type PrimitiveStringSerializer struct{}
|
|
|
|
|
|
|
|
func (pss PrimitiveStringSerializer) ValueToString(v any) (string, error) {
|
|
|
|
|
|
|
|
inType := reflect.TypeOf(v)
|
|
|
|
|
|
|
|
if inType.Kind() == reflect.Ptr && langext.IsNil(v) {
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if inType.Kind() == reflect.Ptr {
|
|
|
|
rval1 := reflect.ValueOf(v)
|
|
|
|
rval2 := rval1.Elem()
|
|
|
|
rval3 := rval2.Interface()
|
|
|
|
return pss.ValueToString(rval3)
|
|
|
|
}
|
|
|
|
|
|
|
|
if conv, ok := primitiveSerializer[inType]; ok {
|
|
|
|
return conv.ToString(v)
|
|
|
|
}
|
|
|
|
|
|
|
|
for convType, conv := range primitiveSerializer {
|
|
|
|
if castV, ok := TryCastType(v, convType); ok {
|
|
|
|
return conv.ToString(castV)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", errors.New(fmt.Sprintf("failed to find a matching generic <toString> conversion fo type %T", v))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (pss PrimitiveStringSerializer) ValueFromString(str string, outType reflect.Type) (any, error) {
|
|
|
|
|
2023-06-07 17:22:38 +02:00
|
|
|
if str == "" {
|
2023-06-07 16:58:17 +02:00
|
|
|
return reflect.Zero(outType).Interface(), nil // = nil.(outType), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if outType.Kind() == reflect.Ptr {
|
|
|
|
|
|
|
|
innerValue, err := pss.ValueFromString(str, outType.Elem())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// this weird piece of shit converts innerValue to &innerValue (while keeping types)
|
|
|
|
|
|
|
|
rval1 := reflect.ValueOf(innerValue)
|
|
|
|
rval2 := rval1.Convert(outType.Elem())
|
|
|
|
rval3 := reflect.New(outType.Elem())
|
|
|
|
rval3.Elem().Set(rval2)
|
|
|
|
rval4 := rval3.Interface()
|
|
|
|
|
|
|
|
return rval4, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if conv, ok := primitiveSerializer[outType]; ok {
|
|
|
|
return conv.FromString(str)
|
|
|
|
}
|
|
|
|
|
|
|
|
emptyResultVal := reflect.Zero(outType).Interface()
|
|
|
|
|
|
|
|
for convType, conv := range primitiveSerializer {
|
|
|
|
if _, ok := TryCastType(emptyResultVal, convType); ok {
|
|
|
|
if convVal, err := conv.FromString(str); err == nil {
|
|
|
|
if resVal, ok := TryCastType(convVal, outType); ok {
|
|
|
|
return resVal, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", errors.New(fmt.Sprintf("failed to find a matching generic <toString> conversion fo type %s", outType.String()))
|
|
|
|
}
|