github.com/xmidt-org/webpa-common@v1.11.9/xerrors/xerrors.go (about) 1 package xerrors 2 3 import ( 4 "errors" 5 "fmt" 6 "reflect" 7 ) 8 9 func getError(unknown interface{}) error { 10 return findErrorInValue(reflect.ValueOf(unknown)) 11 } 12 13 // findErrorInValue will try and genereate and error object either from the object itself if it can be created or its 14 // fields. 15 func findErrorInValue(v reflect.Value) error { 16 if v.Kind() == reflect.Invalid { 17 return nil 18 } 19 20 if v.CanInterface() { 21 // the object can be created, hopefully we can cast it to an error. 22 if _, ok := v.Interface().(*error); ok { 23 return findErrorInValue(v.Elem()) 24 } else if err, ok := v.Interface().(error); ok { 25 if v.Type().String() == "*errors.errorString" { 26 return err 27 } 28 if v.Kind() == reflect.Interface { 29 return err 30 } else if v.Kind() == reflect.Ptr { 31 return findErrorInValue(v.Elem()) 32 } 33 return err 34 } else if v.Kind() == reflect.Ptr { // We can't cast it to an error, check the pointer. 35 return findErrorInValue(v.Elem()) 36 } else if v.Kind() == reflect.Struct { // We can't cast it to an error, check the fields. 37 return iterateFields(v) 38 } 39 } else if v.Kind() == reflect.Ptr && v.Type().String() == "*errors.errorString" { // the base case. AKA errors.New("error") 40 return errors.New(fmt.Sprintf("%s", v.Elem().Field(0))) 41 } else if v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface { // try what the object contains. 42 return findErrorInValue(v.Elem()) 43 } else if v.Kind() == reflect.Struct { // try the fields 44 return iterateFields(v) 45 } 46 47 // an error could not be created 48 return nil 49 } 50 51 // FirstCause will find the first cause of the error. This is making the assumption that the errors are cascaded. 52 // aka an Error struct will contain an error and so on. 53 func FirstCause(err error) error { 54 return findRootError(err) 55 } 56 57 // findRootError will iterate over all the fields and the fields of those fields to generate an error. If no error is 58 // found <nil> will be returned 59 func findRootError(unknown interface{}) error { 60 if unknown == nil { 61 return nil 62 } 63 64 v := reflect.ValueOf(unknown) 65 currentLevel := findErrorInValue(v) 66 var tempErr error 67 68 if v.Kind() == reflect.Ptr { // check if itself and then the fields 69 e := findErrorInValue(v.Elem()) 70 if e != nil { 71 e = findRootError(e) 72 if e != nil { 73 tempErr = e 74 } 75 } 76 } else if v.Kind() == reflect.Struct { // check the fields 77 e := iterateFields(v) 78 if e != nil { 79 e = findRootError(e) 80 if e != nil { 81 tempErr = e 82 } 83 } 84 } 85 86 if tempErr != nil { 87 return tempErr 88 } 89 90 return currentLevel 91 } 92 93 // iterateFields iterates over the variables of the struct and tries to create an error object from the field. 94 // :warning: only can be called where v.Kind() == reflect.Struct 95 func iterateFields(v reflect.Value) error { 96 var tempErr error 97 98 for i := 0; i < v.NumField(); i++ { 99 f := v.Field(i) 100 e := findErrorInValue(f) 101 if e != nil { 102 tempErr = e 103 } 104 if f.CanInterface() { // try and create an error if we can turn it into an interface{} 105 e = findRootError(f.Interface()) 106 if e != nil { 107 tempErr = e 108 } 109 } else if f.Kind() == reflect.Interface { // try the object the field contains 110 f = f.Elem() 111 e = findErrorInValue(f) 112 if e != nil { 113 tempErr = e 114 } 115 } 116 } 117 118 return tempErr 119 }