eris
eris copied to clipboard
Implement new API (from errgroup and multierror) so eris can become ultimate error package.
Is your feature request related to a problem? Please describe.
Implement new API to have only one error package to manage all common errors related cases/problems. With this change, I don't have to import other errors packages and eris would become the ultimate solution for almost all error handling cases.
Describe the solution you'd like
Add/Import more functions and functionalities to this pkg. I've prepared a go docs:
// Wraps adds additional context to all error types while maintaining the type of the original error.
//
// This is a convenience method for wrapping errors with stack trace and is otherwise the same as Wrap(err, "").
func Wraps(err error) error
// Append is a helper function that will append more errors onto an Error in order to create a larger multi-error.
func Append(err error, errs ...error) error
// WrappedErrors returns the list of errors that this Error is wrapping.
func WrappedErrors(err error) []error
// Group is a collection of goroutines that returns errors that need to be coalesced.
type Group struct {
// if true group will continue execution until the last goroutine is finished.
// It will return multierror in case of more than one error, which can be
// turn into the slice with WrappedErrors.
ContinueOnError bool
}
// WithContext returns a new Group and an associated Context derived from ctx.
func WithContext(ctx context.Context) (*Group, context.Context)
// Go calls the given function in a new goroutine. It can work in two modes
//
// 1. The first call to return a non-nil error cancels the group
//
// 2. If the function returns an error it is added to the
// group multierror which is returned by Wait.
//
// You can control this behavour by setting ContinueOnError in Group.
func (g *Group) Go(f func() error)
// Wait blocks until all function calls from the Go method have returned, then returns either
// the first non-nill error (if any) or multierror. This depends on ContinueOnError setting.
func (g *Group) Wait() error
Describe alternatives you've considered
Still using other packages like github.com/hashicorp/go-multierror
or golang.org/x/sync/errgroup
Additional context
I know maybe this Issue should be split into smaller ones but I thought it would be easier to gather all this into one Issue.
One real example that shows how multerror
and Wraps
can be used.
// This is a version with comments. Below you can find two versions to compare readability.
func (p *Postgres) ExecuteInTx(ctx context.Context, fn func(tx *Tx) error) error {
tx, err := p.BeginTx(ctx)
if err != nil {
// just save a stacktrace without any message
// it would be nice to have a function like pkgerros WithStack()
// maybe with shorter name (eris.Stack() or eris.Wraps()),
// to tell that I want to capture only a stack without any message.
// right now I use Wrap for this.
return eris.Wrap(err, "")
}
if err := fn(tx); err != nil {
if rerr := tx.Rollback(ctx); rerr != nil {
// "github.com/hashicorp/go-multierror"
// It would be great to have api to combine
// multiple errors into one. Ofc I can use
// eris.Wrap here but it seems a bit overkill
// to have two calls in stacktrace when in fact
// I just need two error messages combined into one.
// This can be used also in many other places
err = multierror.Append(err, rerr)
}
return eris.Wrap(err, "") // eris.Wraps()
}
return eris.Wrap(tx.Commit(ctx), "") // eris.Wraps())
}
// This is version with external package and Wrap
func (p *Postgres) ExecuteInTx(ctx context.Context, fn func(tx *Tx) error) error {
tx, err := p.BeginTx(ctx)
if err != nil {
return eris.Wrap(err, "")
}
if err := fn(tx); err != nil {
if rerr := tx.Rollback(ctx); rerr != nil {
err = multierror.Append(err, rerr)
}
return eris.Wrap(err, "")
}
return eris.Wrap(tx.Commit(ctx), "")
}
// This is version with eris only.
func (p *Postgres) ExecuteInTx(ctx context.Context, fn func(tx *Tx) error) error {
tx, err := p.BeginTx(ctx)
if err != nil {
return eris.Wraps(err)
}
if err := fn(tx); err != nil {
if rerr := tx.Rollback(ctx); rerr != nil {
err = eris.Append(err, rerr)
}
return eris.Wraps(err)
}
return eris.Wraps(tx.Commit(ctx))
}
hey @krhubert, thanks for submitting this! it's going to take a while to consider all of this but at first glance, i'm hesitant about some of these suggestions. will write up a more detailed response and break this into separate issues once i get a chance. thanks again!
Thanks for submitting the PR. Will take a look over the weekend and get back to you
On Mon, Oct 18, 2021 at 10:35 AM Vera Harless @.***> wrote:
hey @krhubert https://github.com/krhubert, thanks for submitting this! it's going to take a while to consider all of this but at first glance, i'm hesitant about some of these suggestions. will write up a more detailed response and break this into separate issues once i get a chance. thanks again!
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/rotisserie/eris/issues/101#issuecomment-945841410, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAKETUQEN6DSINTFMAVKG6DUHQWBNANCNFSM5F7ITZZQ .
+1 For the append functionality, we ran into this exact use case, where we'd like to wrap an error with another error of ours so we can deal with it ourselves up the stack in a more generic fashion.
i.e. we want to map our errors to an http response at some point, so it would be very useful if one could do things like this:
object, err := library.GetObject(someID)
if errors.Is(err, library.SpecificNotFoundErr) {
return eris.Append(err, GenericNotFoundErr)
}
Which would make it very easy to do things like this.
Go 1.20 added new method Join
to the errors
package: https://pkg.go.dev/errors#Join