github.com/alkemics/goflow@v0.2.1/errors.go (about) 1 package goflow 2 3 import ( 4 "errors" 5 "fmt" 6 "regexp" 7 "strconv" 8 "strings" 9 ) 10 11 type Error struct { 12 Filename string 13 14 Err error 15 } 16 17 func (e Error) Error() string { 18 return fmt.Sprintf("%s: %s", e.Filename, e.Err.Error()) 19 } 20 21 func (e Error) Unwrap() error { 22 return e.Err 23 } 24 25 func (e Error) Format(s fmt.State, verb rune) { 26 if verb != 'v' || (!s.Flag('+')) { 27 fmt.Fprint(s, e.Error()) 28 return 29 } 30 31 errMsg := fmt.Sprintf(" %v", e.Err) 32 33 var multiErr MultiError 34 if errors.As(e.Err, &multiErr) { 35 flat := multiErr.flatten() 36 strs := make([]string, len(flat.Errs)) 37 for i, err := range flat.Errs { 38 strs[i] = fmt.Sprintf(" %v", err) 39 } 40 errMsg = strings.Join(strs, "\n") 41 } 42 43 fmt.Fprintf(s, `%s: 44 %v 45 `, 46 e.Filename, 47 errMsg, 48 ) 49 } 50 51 type MultiError struct { 52 Errs []error 53 } 54 55 func (e MultiError) Error() string { 56 if len(e.Errs) == 1 { 57 return e.Errs[0].Error() 58 } 59 60 strs := make([]string, len(e.Errs)) 61 for i, err := range e.Errs { 62 strs[i] = err.Error() 63 } 64 return fmt.Sprintf("[%s]", strings.Join(strs, ", ")) 65 } 66 67 func (e MultiError) Is(err error) bool { 68 for _, sub := range e.Errs { 69 if errors.Is(sub, err) { 70 return true 71 } 72 } 73 return false 74 } 75 76 func (e MultiError) As(target interface{}) bool { 77 for _, sub := range e.Errs { 78 if errors.As(sub, target) { 79 return true 80 } 81 } 82 return false 83 } 84 85 func (e MultiError) flatten() MultiError { 86 errs := make([]error, 0, len(e.Errs)) 87 for _, err := range e.Errs { 88 var me MultiError 89 if errors.As(err, &me) { 90 errs = append(errs, me.flatten().Errs...) 91 } else { 92 errs = append(errs, err) 93 } 94 } 95 return MultiError{Errs: errs} 96 } 97 98 type GraphError struct { 99 Wrapper string 100 Err error 101 } 102 103 func (e GraphError) Error() string { 104 return fmt.Sprintf("%v (%s)", e.Err, e.Wrapper) 105 } 106 107 func (e GraphError) Unwrap() error { 108 return e.Err 109 } 110 111 type NodeError struct { 112 ID string 113 Wrapper string 114 115 Err error 116 } 117 118 func (e NodeError) Error() string { 119 return fmt.Sprintf("%s: %v (%s)", e.ID, e.Err, e.Wrapper) 120 } 121 122 func (e NodeError) Unwrap() error { 123 return e.Err 124 } 125 126 var yamlErrorRegex = regexp.MustCompile(`(?s)(?:yaml: )?(?:unmarshal errors:.)?line (\d+): (.*)`) 127 128 type YAMLError struct { 129 Line int 130 Err error 131 } 132 133 func (e YAMLError) Error() string { 134 return fmt.Sprintf("line %d: %v (yaml)", e.Line, e.Err.Error()) 135 } 136 137 func (e YAMLError) Unwrap() error { 138 return e.Err 139 } 140 141 func ParseYAMLError(err error) error { 142 if err == nil { 143 return nil 144 } 145 146 match := yamlErrorRegex.FindStringSubmatch(err.Error()) 147 if len(match) == 0 { 148 return err 149 } 150 151 line, _ := strconv.Atoi(match[1]) 152 return YAMLError{ 153 Line: line, 154 Err: errors.New(strings.TrimSpace(match[2])), 155 } 156 }