2023-02-15 16:04:19 +01:00
|
|
|
package exerr
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"github.com/rs/zerolog/log"
|
2023-08-09 14:40:16 +02:00
|
|
|
"gogs.mikescher.com/BlackForestBytes/goext/enums"
|
2023-02-15 16:04:19 +01:00
|
|
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
//
|
|
|
|
// These are wrapper objects, because for some metadata-types we need to serialize a bit more complex data
|
|
|
|
// (eg thy actual type for ID objects, or the json representation for any types)
|
|
|
|
//
|
|
|
|
|
|
|
|
type IDWrap struct {
|
|
|
|
Type string
|
|
|
|
Value string
|
|
|
|
IsNil bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func newIDWrap(val fmt.Stringer) IDWrap {
|
|
|
|
t := fmt.Sprintf("%T", val)
|
|
|
|
arr := strings.Split(t, ".")
|
|
|
|
if len(arr) > 0 {
|
|
|
|
t = arr[len(arr)-1]
|
|
|
|
}
|
|
|
|
|
|
|
|
if langext.IsNil(val) {
|
|
|
|
return IDWrap{Type: t, Value: "", IsNil: true}
|
|
|
|
}
|
|
|
|
|
|
|
|
v := val.String()
|
|
|
|
return IDWrap{Type: t, Value: v, IsNil: false}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w IDWrap) Serialize() string {
|
|
|
|
if w.IsNil {
|
|
|
|
return "!nil" + ":" + w.Type
|
|
|
|
}
|
|
|
|
return w.Type + ":" + w.Value
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w IDWrap) String() string {
|
|
|
|
if w.IsNil {
|
|
|
|
return w.Type + "<<nil>>"
|
|
|
|
}
|
|
|
|
return w.Type + "(" + w.Value + ")"
|
|
|
|
}
|
|
|
|
|
|
|
|
func deserializeIDWrap(v string) IDWrap {
|
|
|
|
r := strings.SplitN(v, ":", 2)
|
|
|
|
|
|
|
|
if len(r) == 2 && r[0] == "!nil" {
|
|
|
|
return IDWrap{Type: r[1], Value: v, IsNil: true}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(r) == 0 {
|
|
|
|
return IDWrap{}
|
|
|
|
} else if len(r) == 1 {
|
|
|
|
return IDWrap{Type: "", Value: v, IsNil: false}
|
|
|
|
} else {
|
|
|
|
return IDWrap{Type: r[0], Value: r[1], IsNil: false}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type AnyWrap struct {
|
|
|
|
Type string
|
|
|
|
Json string
|
|
|
|
IsError bool
|
|
|
|
IsNil bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func newAnyWrap(val any) (result AnyWrap) {
|
|
|
|
result = AnyWrap{Type: "", Json: "", IsError: true, IsNil: false} // ensure a return in case of recover()
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
// send error should never crash our program
|
|
|
|
log.Error().Interface("err", err).Msg("Panic while trying to marshal anywrap ( bmerror.Interface )")
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
t := fmt.Sprintf("%T", val)
|
|
|
|
|
|
|
|
if langext.IsNil(val) {
|
|
|
|
return AnyWrap{Type: t, Json: "", IsError: false, IsNil: true}
|
|
|
|
}
|
|
|
|
|
|
|
|
j, err := json.Marshal(val)
|
|
|
|
if err == nil {
|
|
|
|
return AnyWrap{Type: t, Json: string(j), IsError: false, IsNil: false}
|
|
|
|
} else {
|
|
|
|
return AnyWrap{Type: t, Json: "", IsError: true, IsNil: false}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w AnyWrap) Serialize() string {
|
|
|
|
if w.IsError {
|
|
|
|
return "ERR" + ":" + w.Type + ":" + w.Json
|
|
|
|
} else if w.IsNil {
|
|
|
|
return "NIL" + ":" + w.Type + ":" + w.Json
|
|
|
|
} else {
|
|
|
|
return "OK" + ":" + w.Type + ":" + w.Json
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w AnyWrap) String() string {
|
|
|
|
if w.IsError {
|
|
|
|
return "(error)"
|
|
|
|
} else if w.IsNil {
|
|
|
|
return "(nil)"
|
|
|
|
} else {
|
|
|
|
return w.Json
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func deserializeAnyWrap(v string) AnyWrap {
|
|
|
|
r := strings.SplitN(v, ":", 3)
|
|
|
|
if len(r) != 3 {
|
|
|
|
return AnyWrap{IsError: true, Type: "", Json: "", IsNil: false}
|
|
|
|
} else {
|
|
|
|
if r[0] == "OK" {
|
|
|
|
return AnyWrap{IsError: false, Type: r[1], Json: r[2], IsNil: false}
|
|
|
|
} else if r[0] == "ERR" {
|
|
|
|
return AnyWrap{IsError: true, Type: r[1], Json: r[2], IsNil: false}
|
|
|
|
} else if r[0] == "NIL" {
|
|
|
|
return AnyWrap{IsError: false, Type: r[1], Json: "", IsNil: true}
|
|
|
|
} else {
|
|
|
|
return AnyWrap{IsError: true, Type: "", Json: "", IsNil: false}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-08-09 14:40:16 +02:00
|
|
|
|
|
|
|
type EnumWrap struct {
|
|
|
|
Type string
|
|
|
|
ValueString string
|
|
|
|
ValueRaw enums.Enum // `ValueRaw` is lost during serialization roundtrip
|
|
|
|
IsNil bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func newEnumWrap(val enums.Enum) EnumWrap {
|
|
|
|
t := fmt.Sprintf("%T", val)
|
|
|
|
arr := strings.Split(t, ".")
|
|
|
|
if len(arr) > 0 {
|
|
|
|
t = arr[len(arr)-1]
|
|
|
|
}
|
|
|
|
|
|
|
|
if langext.IsNil(val) {
|
|
|
|
return EnumWrap{Type: t, ValueString: "", ValueRaw: val, IsNil: true}
|
|
|
|
}
|
|
|
|
|
|
|
|
if enumstr, ok := val.(enums.StringEnum); ok {
|
|
|
|
return EnumWrap{Type: t, ValueString: enumstr.String(), ValueRaw: val, IsNil: false}
|
|
|
|
}
|
|
|
|
|
|
|
|
return EnumWrap{Type: t, ValueString: fmt.Sprintf("%v", val), ValueRaw: val, IsNil: false}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w EnumWrap) Serialize() string {
|
|
|
|
if w.IsNil {
|
|
|
|
return "!nil" + ":" + w.Type
|
|
|
|
}
|
|
|
|
return w.Type + ":" + w.ValueString
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w EnumWrap) String() string {
|
|
|
|
if w.IsNil {
|
|
|
|
return w.Type + "<<nil>>"
|
|
|
|
}
|
|
|
|
return "[" + w.Type + "] " + w.ValueString
|
|
|
|
}
|
|
|
|
|
|
|
|
func deserializeEnumWrap(v string) EnumWrap {
|
|
|
|
r := strings.SplitN(v, ":", 2)
|
|
|
|
|
|
|
|
if len(r) == 2 && r[0] == "!nil" {
|
|
|
|
return EnumWrap{Type: r[1], ValueString: v, ValueRaw: nil, IsNil: true}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(r) == 0 {
|
|
|
|
return EnumWrap{}
|
|
|
|
} else if len(r) == 1 {
|
|
|
|
return EnumWrap{Type: "", ValueString: v, ValueRaw: nil, IsNil: false}
|
|
|
|
} else {
|
|
|
|
return EnumWrap{Type: r[0], ValueString: r[1], ValueRaw: nil, IsNil: false}
|
|
|
|
}
|
|
|
|
}
|