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)