-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
A Tortilla can be created from an error or an existing Tortilla. An error can wrap a Tortilla, or simply added in the stack. A Tortilla can be rolled-out to be displayed as a human-readable string.
- Loading branch information
Martial Saunois
authored
Dec 7, 2021
1 parent
cd31dd4
commit 104f862
Showing
11 changed files
with
614 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
name: CI | ||
|
||
on: | ||
push: | ||
branches: | ||
- main | ||
pull_request: | ||
branches: | ||
- main | ||
|
||
jobs: | ||
tests: | ||
runs-on: ubuntu-20.04 | ||
strategy: | ||
matrix: | ||
go: [ '1.16', '1.17' ] | ||
name: Run tests on Go ${{ matrix.go }} | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- name: Setup go | ||
uses: actions/setup-go@v2 | ||
with: | ||
go-version: ${{ matrix.go }} | ||
- run: go test -v ./... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
* @MartialGeek | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,173 @@ | ||
# tortilla | ||
A golang package to wrap our errors as easily as your tortillas. | ||
![Tortilla logo](./tortilla.png) | ||
*(Author: [Micheile](https://unsplash.com/@micheile), original: https://unsplash.com/photos/1zyj8nOdwPs)* | ||
|
||
# Tortilla | ||
|
||
A Go package to wrap your errors as easily as your tortillas. *Bon appétit!* | ||
|
||
# Purpose | ||
|
||
Errors in Go is a very divisive topic. There are pros and cons, and for me the thing I really miss | ||
compared to "traditional" exceptions mechanism is the ability to keep an history of the errors you | ||
handled in the lifetime of your program. Although it is possible to wrap or embed errors to keep an | ||
information of what happened in a lower level in vanilla Go with `fmt.Errorf`, I found really | ||
hard to set up a strong but simple, standardized but not too "cumbersome" error handling. | ||
|
||
That's why a thought about creating a package to make my error handling easier, and because Go wraps | ||
errors in other errors, (and because I love Tex-Mex cuisine as well), I liked the idea of wrapping all | ||
that things with big Tortillas! | ||
|
||
# Examples | ||
|
||
OK, enough talk. Let's take some examples to illustrate what you can do with a Tortilla! | ||
These examples can be found in [the examples directory of the project](./examples). | ||
|
||
## Wrap and pretty print | ||
|
||
In this example we simulate a call to a function that fetches data from a DB and stores it in cache. | ||
The cache returns an error, a Tortilla is created from it and we wrap with our business error | ||
`errDataFetching`. Then we can use `errors.Is` to make a decision regarding the error. Please note that | ||
only the last error used to wrap a Tortilla can be matched by `errors.Is` or `errors.As`. | ||
|
||
```go | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"log" | ||
|
||
"github.com/MartialGeek/tortilla" | ||
) | ||
|
||
var ( | ||
errDataFetching = errors.New("unable to fetch data") | ||
) | ||
|
||
func main() { | ||
err := fetchSomeData() | ||
if err != nil { | ||
if errors.Is(err, errDataFetching) { | ||
log.Fatal(tortilla.New(err).RollOut().PrettyPrint()) | ||
} | ||
|
||
log.Println("unknown error:", err) | ||
} | ||
} | ||
|
||
func fetchSomeData() error { | ||
cacheErr := cache() | ||
if cacheErr != nil { | ||
return tortilla.New(cacheErr).Wrap(errDataFetching) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func cache() error { | ||
return errors.New("some cache error") | ||
} | ||
``` | ||
|
||
Here we simply re-create our Tortilla with `tortilla.New` to call `RollOut().PrettyPrint()`. This will | ||
generate a string with a visual representation of the encountered errors in the reverse order of creation: | ||
|
||
``` | ||
unable to fetch data: | ||
some cache error: | ||
``` | ||
|
||
## Adding errors and hierarchy | ||
|
||
Now what if you want to add some errors without using them to wrap your Tortilla? | ||
|
||
```go | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"log" | ||
|
||
"github.com/MartialGeek/tortilla" | ||
) | ||
|
||
var ( | ||
errObfuscate = errors.New("unable to obsufcate data") | ||
errEncode = errors.New("encoding failed") | ||
) | ||
|
||
func main() { | ||
err := obsufcate() | ||
if err != nil { | ||
if errors.Is(err, errObfuscate) { | ||
log.Fatal(tortilla.New(err).RollOut().PrettyPrint()) | ||
} | ||
} | ||
} | ||
|
||
func obsufcate() error { | ||
err := encode() | ||
if err != nil { | ||
err = tortilla. | ||
New(errObfuscate). | ||
Add(err). | ||
Add(errors.New("some context of what happened")) | ||
} | ||
|
||
return err | ||
} | ||
|
||
func encode() error { | ||
err := encrypt() | ||
if err != nil { | ||
err = tortilla.New(errEncode).Add(err) | ||
} | ||
|
||
return err | ||
} | ||
|
||
func encrypt() error { | ||
return errors.New("encryption error") | ||
} | ||
``` | ||
|
||
* First, in `encode` function we create a Tortilla `errEncode` and we add the encryption error into it. | ||
* Then in `obsufcate` function a new Tortilla `errObfuscate` wraps the previous Tortilla and add another | ||
error to explain what happaned. | ||
* Finally in `main` the error matched `errObfuscate` and the Tortilla is rolled-out to be displayed in a | ||
human-readable string: | ||
|
||
``` | ||
unable to obsufcate data: | ||
....some context of what happened | ||
....encoding failed: encryption error. | ||
``` | ||
|
||
Here we see the hierachy of the errors: the first line is the last error used to wrap the Tortilla. | ||
The second and third are shifted to identify them as "children" of the first one. | ||
|
||
As you can see, the third line contains the encoding error followed by the encryption error. It's because | ||
we added the error returned by `encode`, which was flattened. | ||
|
||
If you want to keep the exact hierachy, you must create a Torilla from the returned error, and then wrapping | ||
with your own error: | ||
|
||
```go | ||
func obsufcate() error { | ||
err := encode() | ||
if err != nil { | ||
err = tortilla. | ||
New(err). | ||
Wrap(errObfuscate). | ||
Add(errors.New("some context of what happened")) | ||
} | ||
|
||
return err | ||
} | ||
``` | ||
|
||
``` | ||
unable to obsufcate data: | ||
....some context of what happened | ||
encoding failed: | ||
....encryption error | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"log" | ||
|
||
"github.com/MartialGeek/tortilla" | ||
) | ||
|
||
var ( | ||
errObfuscate = errors.New("unable to obsufcate data") | ||
errEncode = errors.New("encoding failed") | ||
) | ||
|
||
func main() { | ||
err := obsufcate() | ||
if err != nil { | ||
if errors.Is(err, errObfuscate) { | ||
log.Fatal(tortilla.New(err).RollOut().PrettyPrint()) | ||
} | ||
} | ||
} | ||
|
||
func obsufcate() error { | ||
err := encode() | ||
if err != nil { | ||
err = tortilla. | ||
New(errObfuscate). | ||
Add(err). | ||
Add(errors.New("some context of what happened")) | ||
} | ||
|
||
return err | ||
} | ||
|
||
func encode() error { | ||
err := encrypt() | ||
if err != nil { | ||
err = tortilla.New(errEncode).Add(err) | ||
} | ||
|
||
return err | ||
} | ||
|
||
func encrypt() error { | ||
return errors.New("encryption error") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"log" | ||
|
||
"github.com/MartialGeek/tortilla" | ||
) | ||
|
||
var ( | ||
errObfuscate = errors.New("unable to obsufcate data") | ||
errEncode = errors.New("encoding failed") | ||
) | ||
|
||
func main() { | ||
err := obsufcate() | ||
if err != nil { | ||
if errors.Is(err, errObfuscate) { | ||
log.Fatal(tortilla.New(err).RollOut().PrettyPrint()) | ||
} | ||
} | ||
} | ||
|
||
func obsufcate() error { | ||
err := encode() | ||
if err != nil { | ||
err = tortilla. | ||
New(err). | ||
Wrap(errObfuscate). | ||
Add(errors.New("some context of what happened")) | ||
} | ||
|
||
return err | ||
} | ||
|
||
func encode() error { | ||
err := encrypt() | ||
if err != nil { | ||
err = tortilla.New(errEncode).Add(err) | ||
} | ||
|
||
return err | ||
} | ||
|
||
func encrypt() error { | ||
return errors.New("encryption error") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"log" | ||
|
||
"github.com/MartialGeek/tortilla" | ||
) | ||
|
||
var ( | ||
errDataFetching = errors.New("unable to fetch data") | ||
) | ||
|
||
func main() { | ||
err := fetchSomeData() | ||
if err != nil { | ||
if errors.Is(err, errDataFetching) { | ||
log.Fatal(tortilla.New(err).RollOut().PrettyPrint()) | ||
} | ||
|
||
log.Println("unknown error:", err) | ||
} | ||
} | ||
|
||
func fetchSomeData() error { | ||
cacheErr := cache() | ||
if cacheErr != nil { | ||
return tortilla.New(cacheErr).Wrap(errDataFetching) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func cache() error { | ||
return errors.New("some cache error") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
module github.com/MartialGeek/tortilla | ||
|
||
go 1.17 | ||
|
||
require github.com/stretchr/testify v1.7.0 | ||
|
||
require ( | ||
github.com/davecgh/go-spew v1.1.0 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | ||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | ||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.