gopkg.in/hedzr/errors.v3@v3.3.1/README.md (about) 1 # errors.v3 2 3 [![Go](https://github.com/hedzr/errors/actions/workflows/go.yml/badge.svg)](https://github.com/hedzr/errors/actions/workflows/go.yml) 4 [![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/hedzr/errors.svg?label=release)](https://gopkg.in/hedzr/errors.v3) 5 [![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://pkg.go.dev/gopkg.in/hedzr/errors.v3) 6 [![Go Report Card](https://goreportcard.com/badge/github.com/hedzr/errors)](https://goreportcard.com/report/github.com/hedzr/errors) 7 [![Coverage Status](https://coveralls.io/repos/github/hedzr/errors/badge.svg)](https://coveralls.io/github/hedzr/errors) 8 [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fhedzr%2Ferrors.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fhedzr%2Ferrors?ref=badge_shield) 9 10 Wrapped errors and more for golang developing (not just for go1.11, go1.13, and go1.20+). 11 12 `hedzr/errors` provides the compatibilities to your old project up to go 1.20. 13 14 `hedzr/errors` provides some extra enhancements for better context environment saving on error occurred. 15 16 ## Features 17 18 - Simple migrating way from std errors: all of standard functions have been copied to 19 - Better `New()`: 20 - format message inline: `err := errors.New("hello %s", "world")` 21 - format with `WithXXX`: `err := errors.New(errors.WithErrors(errs...))` 22 - cascade format: `err := errors.New().WithErrors(errs...)` 23 - Stacktrace awareness 24 - Container for canning errors: [Error Container (Inner/Nested)](#error-container-innernested) 25 - error template: [Format message instantly but the text template can be given at beginning](#error-template) 26 - Codes: treat a number as an error object 27 - Unwrap inner canned errors one by one 28 - No mental burden 29 30 ### Others 31 32 ## History 33 34 - v3.3.1 35 - fixed Iss() couldn't test the others except the first error. 36 37 - v3.3.0 38 - added `Iss(err, errs...)` to test if any of errors are included in 'err'. 39 - improved As/Is/Unwrap to fit for new joint error since go1.20 40 - added causes2.Clear, ... 41 - improved Error() string 42 - reviewed and re-published this repo from v3.3 43 44 - v3.1.9 45 - fixed error.Is deep test to check two errors' message text contents if matched 46 - fixed errors.v3.Join when msg is not empty in an err obj 47 - fixed causes.WithErrors(): err obj has been ignored even if its message is not empty 48 49 - OLDER in [CHANGELOG](https://github.com/hedzr/errors/blob/master/CHANGELOG) 50 51 ## Compatibilities 52 53 These features are supported for compatibilities. 54 55 ### stdlib `errors' compatibilities 56 57 - `func As(err error, target interface{}) bool` 58 - `func Is(err, target error) bool` 59 - `func New(text string) error` 60 - `func Unwrap(err error) error` 61 - `func Join(errs ...error) error` 62 63 ### `pkg/errors` compatibilities 64 65 - `func Wrap(err error, message string) error` 66 - `func Cause(err error) error`: unwraps recursively, just like Unwrap() 67 - [x] `func Cause1(err error) error`: unwraps just one level 68 - `func WithCause(cause error, message string, args ...interface{}) error`, = `Wrap` 69 - supports Stacktrace 70 - in an error by `Wrap()`, stacktrace wrapped; 71 - for your error, attached by `WithStack(cause error)`; 72 73 ### Some Enhancements 74 75 - `Iss(err error, errs ...error) bool` 76 - `AsSlice(errs []error, target interface{}) bool` 77 - `IsAnyOf(err error, targets ...error) bool` 78 - `IsSlice(errs []error, target error) bool` 79 - `TypeIs(err, target error) bool` 80 - `TypeIsSlice(errs []error, target error) bool` 81 - `Join(errs ...error) error` 82 - `DumpStacksAsString(allRoutines bool) string` 83 - `CanAttach(err interface{}) (ok bool)` 84 - `CanCause(err interface{}) (ok bool)` 85 - `CanCauses(err interface{}) (ok bool)` 86 - `CanUnwrap(err interface{}) (ok bool)` 87 - `CanIs(err interface{}) (ok bool)` 88 - `CanAs(err interface{}) (ok bool)` 89 - `Causes(err error) (errs []error)` 90 91 ## Best Practices 92 93 ### Basics 94 95 ```go 96 package test 97 98 import ( 99 "gopkg.in/hedzr/errors.v3" 100 "io" 101 "reflect" 102 "testing" 103 ) 104 105 func TestForExample(t *testing.T) { 106 fn := func() (err error) { 107 ec := errors.New("some tips %v", "here") 108 defer ec.Defer(&err) 109 110 // attaches much more errors 111 for _, e := range []error{io.EOF, io.ErrClosedPipe} { 112 ec.Attach(e) 113 } 114 } 115 116 err := fn() 117 t.Logf("failed: %+v", err) 118 119 // use another number different to default to skip the error frames 120 err = errors. 121 Skip(3). // from on Skip() 122 WithMessage("some tips %v", "here").Build() 123 t.Logf("failed: %+v", err) 124 125 err = errors. 126 Message("1"). // from Message() on 127 WithSkip(0). 128 WithMessage("bug msg"). 129 Build() 130 t.Logf("failed: %+v", err) 131 132 err = errors. 133 NewBuilder(). // from NewBuilder() on 134 WithCode(errors.Internal). // add errors.Code 135 WithErrors(io.EOF). // attach inner errors 136 WithErrors(io.ErrShortWrite, io.ErrClosedPipe). 137 Build() 138 t.Logf("failed: %+v", err) 139 140 // As code 141 var c1 errors.Code 142 if errors.As(err, &c1) { 143 println(c1) // = Internal 144 } 145 146 // As inner errors 147 var a1 []error 148 if errors.As(err, &a1) { 149 println(len(a1)) // = 3, means [io.EOF, io.ErrShortWrite, io.ErrClosedPipe] 150 } 151 // Or use Causes() to extract them: 152 if reflect.DeepEqual(a1, errors.Causes(err)) { 153 t.Fatal("unexpected problem") 154 } 155 156 // As error, the first inner error will be extracted 157 var ee1 error 158 if errors.As(err, &ee1) { 159 println(ee1) // = io.EOF 160 } 161 162 series := []error{io.EOF, io.ErrShortWrite, io.ErrClosedPipe, errors.Internal} 163 var index int 164 for ; ee1 != nil; index++ { 165 ee1 = errors.Unwrap(err) // extract the inner errors one by one 166 if ee1 != nil && ee1 != series[index] { 167 t.Fatalf("%d. cannot extract '%v' error with As(), ee1 = %v", index, series[index], ee1) 168 } 169 } 170 } 171 ``` 172 173 ### Error Container (Inner/Nested) 174 175 ```go 176 func TestContainer(t *testing.T) { 177 // as a inner errors container 178 child := func() (err error) { 179 ec := errors.New("multiple tasks have errors") 180 defer ec.Defer(&err) // package the attached errors as a new one and return it as `err` 181 182 for _, r := range []error{io.EOF, io.ErrShortWrite, io.ErrClosedPipe, errors.Internal} { 183 ec.Attach(r) 184 } 185 186 doWithItem := func(item Item) (err error) { 187 // ... 188 return 189 } 190 for _, item := range SomeItems { 191 // nil will be ignored safely, do'nt worry about invalid attaches. 192 ec.Attach(doWithItem(item)) 193 } 194 195 return 196 } 197 198 err := child() // get the canned errors as a packaged one 199 t.Logf("failed: %+v", err) 200 } 201 ``` 202 203 ### Error Template 204 205 We could *declare* a message template at first and format it with live args 206 to build an error instantly. 207 208 ```go 209 func TestErrorsTmpl(t *testing.T) { 210 errTmpl := errors.New("expecting %v but got %v") 211 212 var err error 213 err = errTmpl.FormatWith("789", "123") 214 t.Logf("The error is: %v", err) 215 err = errTmpl.FormatWith(true, false) 216 t.Logf("The error is: %v", err) 217 } 218 ``` 219 220 `FormatWith` will make new clone from errTmpl so you can use multiple cloned errors thread-safely. 221 222 The derived error instance is the descendant of the error template. 223 This relation can be tested by `errors.IsDescent(errTempl, err)` 224 225 ```go 226 func TestIsDescended(t *testing.T) { 227 err3 := New("any error tmpl with %v") 228 err4 := err3.FormatWith("huahua") 229 if !IsDescended(err3, err4) { 230 t.Fatalf("bad test on IsDescended(err3, err4)") 231 } 232 } 233 ``` 234 235 ### Better format for a nested error 236 237 Since v3.1.1, the better message format will be formatted at Printf("%+v"). 238 239 ```go 240 func TestAs_betterFormat(t *testing.T) { 241 var err = New("Have errors").WithErrors(io.EOF, io.ErrShortWrite, io.ErrNoProgress) 242 t.Logf("%v\n", err) 243 244 var nestNestErr = New("Errors FOUND:").WithErrors(err, io.EOF) 245 var nnnErr = New("Nested Errors:").WithErrors(nestNestErr, strconv.ErrRange) 246 t.Logf("%v\n", nnnErr) 247 t.Logf("%+v\n", nnnErr) 248 } 249 ``` 250 251 The output is: 252 253 ```bash 254 === RUN TestAs_betterFormat 255 causes_test.go:23: Have errors [EOF | short write | multiple Read calls return no data or error] 256 causes_test.go:27: Nested Errors: [Errors FOUND: [Have errors [EOF | short write | multiple Read calls return no data or error] | EOF] | value out of range] 257 causes_test.go:28: Nested Errors: 258 - Errors FOUND: 259 - Have errors 260 - EOF 261 - short write 262 - multiple Read calls return no data or error 263 - EOF 264 - value out of range 265 266 gopkg.in/hedzr/errors%2ev3.TestAs_betterFormat 267 /Volumes/VolHack/work/godev/cmdr-series/libs/errors/causes_test.go:26 268 testing.tRunner 269 /usr/local/go/src/testing/testing.go:1576 270 runtime.goexit 271 /usr/local/go/src/runtime/asm_amd64.s:1598 272 --- PASS: TestAs_betterFormat (0.00s) 273 PASS 274 ``` 275 276 ## LICENSE 277 278 MIT 279 280 ### Scan 281 282 [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fhedzr%2Ferrors.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fhedzr%2Ferrors?ref=badge_large)