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 conversion fo type %T", v)) } func (pss PrimitiveStringSerializer) ValueFromString(str string, outType reflect.Type) (any, error) { if outType.Kind() == reflect.Ptr && str == "" { return reflect.Zero(outType).Interface(), nil // = nil.(outType), nil } if str == "" { return reflect.Zero(outType).Interface(), 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 conversion fo type %s", outType.String())) }