package exerr import ( "bringman.de/common/shared/langext" "encoding/json" "go.mongodb.org/mongo-driver/bson/primitive" "reflect" "time" ) var reflectTypeStr = reflect.TypeOf("") func getForeignMeta(err error) (mm MetaMap) { mm = make(map[string]MetaValue) defer func() { if panicerr := recover(); panicerr != nil { New(ErrPanic, "Panic while trying to get foreign meta"). Str("source", err.Error()). Interface("panic-object", panicerr). Stack(). Print() } }() rval := reflect.ValueOf(err) if rval.Kind() == reflect.Interface || rval.Kind() == reflect.Ptr { rval = reflect.ValueOf(err).Elem() } mm.add("foreign.errortype", MDTString, rval.Type().String()) for k, v := range addMetaPrefix("foreign", getReflectedMetaValues(err, 8)) { mm[k] = v } return mm } func getReflectedMetaValues(value interface{}, remainingDepth int) map[string]MetaValue { if remainingDepth <= 0 { return map[string]MetaValue{} } if langext.IsNil(value) { return map[string]MetaValue{"": {DataType: MDTNil, Value: nil}} } rval := reflect.ValueOf(value) if rval.Type().Kind() == reflect.Ptr { if rval.IsNil() { return map[string]MetaValue{"*": {DataType: MDTNil, Value: nil}} } elem := rval.Elem() return addMetaPrefix("*", getReflectedMetaValues(elem.Interface(), remainingDepth-1)) } if !rval.CanInterface() { return map[string]MetaValue{"": {DataType: MDTString, Value: "<>"}} } raw := rval.Interface() switch ifraw := raw.(type) { case time.Time: return map[string]MetaValue{"": {DataType: MDTTime, Value: ifraw}} case time.Duration: return map[string]MetaValue{"": {DataType: MDTDuration, Value: ifraw}} case int: return map[string]MetaValue{"": {DataType: MDTInt, Value: ifraw}} case int8: return map[string]MetaValue{"": {DataType: MDTInt8, Value: ifraw}} case int16: return map[string]MetaValue{"": {DataType: MDTInt16, Value: ifraw}} case int32: return map[string]MetaValue{"": {DataType: MDTInt32, Value: ifraw}} case int64: return map[string]MetaValue{"": {DataType: MDTInt64, Value: ifraw}} case string: return map[string]MetaValue{"": {DataType: MDTString, Value: ifraw}} case bool: return map[string]MetaValue{"": {DataType: MDTBool, Value: ifraw}} case []byte: return map[string]MetaValue{"": {DataType: MDTBytes, Value: ifraw}} case float32: return map[string]MetaValue{"": {DataType: MDTFloat32, Value: ifraw}} case float64: return map[string]MetaValue{"": {DataType: MDTFloat64, Value: ifraw}} case []int: return map[string]MetaValue{"": {DataType: MDTIntArray, Value: ifraw}} case []int32: return map[string]MetaValue{"": {DataType: MDTInt32Array, Value: ifraw}} case primitive.ObjectID: return map[string]MetaValue{"": {DataType: MDTObjectID, Value: ifraw}} case []string: return map[string]MetaValue{"": {DataType: MDTStringArray, Value: ifraw}} } if rval.Type().Kind() == reflect.Struct { m := make(map[string]MetaValue) for i := 0; i < rval.NumField(); i++ { fieldtype := rval.Type().Field(i) fieldname := fieldtype.Name if fieldtype.IsExported() { for k, v := range addMetaPrefix(fieldname, getReflectedMetaValues(rval.Field(i).Interface(), remainingDepth-1)) { m[k] = v } } } return m } if rval.Type().ConvertibleTo(reflectTypeStr) { return map[string]MetaValue{"": {DataType: MDTString, Value: rval.Convert(reflectTypeStr).String()}} } jsonval, err := json.Marshal(value) if err != nil { panic(err) // gets recovered later up } return map[string]MetaValue{"": {DataType: MDTString, Value: string(jsonval)}} } func addMetaPrefix(prefix string, m map[string]MetaValue) map[string]MetaValue { if len(m) == 1 { for k, v := range m { if k == "" { return map[string]MetaValue{prefix: v} } } } r := make(map[string]MetaValue, len(m)) for k, v := range m { r[prefix+"."+k] = v } return r }