184 lines
5.4 KiB
Go
184 lines
5.4 KiB
Go
package try
|
|
|
|
/*
|
|
try is a fork of lainio's err2 package
|
|
Copyright (c) 2019 Lainio, MIT License
|
|
https://github.com/lainio/err2
|
|
*/
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"runtime/debug"
|
|
)
|
|
|
|
// Empty is a helper method to handle errors of func() (string, error) functions.
|
|
func Empty(_ interface{}, err error) {
|
|
Check(err)
|
|
}
|
|
|
|
// Any is as similar as proposed Go2 Try macro, but it's a function and it
|
|
// returns slice of interfaces. It has quite big performance penalty when
|
|
// compared to Check function.
|
|
func Any(args ...interface{}) []interface{} {
|
|
check(args)
|
|
return args
|
|
}
|
|
|
|
// Check performs the error check for the given argument. If the err is nil,
|
|
// it does nothing. According the measurements, it's as fast as if err != nil
|
|
// {return err} on happy path.
|
|
func Check(err error) {
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// Checks the error status of the last argument. It panics with "wrong
|
|
// signature" if the last calling parameter is not error. In case of error it
|
|
// delivers it by panicking.
|
|
func check(args []interface{}) {
|
|
argCount := len(args)
|
|
last := argCount - 1
|
|
if args[last] != nil {
|
|
err, ok := args[last].(error)
|
|
if !ok {
|
|
panic("wrong signature")
|
|
}
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// Handle is for adding an error handler to a function by defer. It's for
|
|
// functions returning errors them self. For those functions that doesn't
|
|
// return errors there is a Catch function. Note! The handler function f is
|
|
// called only when err != nil.
|
|
func Handle(err *error, f func()) {
|
|
// This and Catch are similar but we need to call recover() here because
|
|
// how it works with defer. We cannot refactor these to use same function.
|
|
|
|
// We put real panic objects back and keep only those which are
|
|
// carrying our errors. We must also call all of the handlers in defer
|
|
// stack.
|
|
switch r := recover(); r.(type) {
|
|
case nil:
|
|
// Defers are in the stack and the first from the stack gets the
|
|
// opportunity to get panic object's error (below). We still must
|
|
// call handler functions to the rest of the handlers if there is
|
|
// an error.
|
|
if *err != nil {
|
|
f()
|
|
}
|
|
case error:
|
|
// We or someone did transport this error thru panic.
|
|
*err = r.(error)
|
|
f()
|
|
default:
|
|
panic(r)
|
|
}
|
|
}
|
|
|
|
// Returnf wraps an error. It's similar to fmt.Errorf, but it's called only if
|
|
// error != nil.
|
|
func Returnf(err *error, format string, args ...interface{}) {
|
|
// This and Handle are similar but we need to call recover here because how
|
|
// it works with defer. We cannot refactor these two to use same function.
|
|
|
|
if r := recover(); r != nil {
|
|
e, ok := r.(error)
|
|
if !ok {
|
|
panic(r) // Not ours, carry on panicking
|
|
}
|
|
*err = fmt.Errorf(format+": %v", append(args, e)...)
|
|
} else if *err != nil { // if other handlers call recovery() we still..
|
|
*err = fmt.Errorf(format+": %v", append(args, *err)...)
|
|
}
|
|
}
|
|
|
|
// Catch is a convenient helper to those functions that doesn't return errors.
|
|
// Go's main function is a good example. Note! There can be only one deferred
|
|
// Catch function per non error returning function. See Handle for more
|
|
// information.
|
|
func Catch(f func(err error)) {
|
|
// This and Handle are similar but we need to call recover here because how
|
|
// it works with defer. We cannot refactor these 2 to use same function.
|
|
|
|
if r := recover(); r != nil {
|
|
e, ok := r.(error)
|
|
if !ok {
|
|
panic(r)
|
|
}
|
|
f(e)
|
|
}
|
|
}
|
|
|
|
// CatchAll is a helper function to catch and write handlers for all errors and
|
|
// all panics thrown in the current go routine.
|
|
func CatchAll(errorHandler func(err error), panicHandler func(v interface{})) {
|
|
// This and Handle are similar but we need to call recover here because how
|
|
// it works with defer. We cannot refactor these 2 to use same function.
|
|
|
|
if r := recover(); r != nil {
|
|
e, ok := r.(error)
|
|
if ok {
|
|
errorHandler(e)
|
|
} else {
|
|
panicHandler(r)
|
|
}
|
|
}
|
|
}
|
|
|
|
// CatchTrace is a helper function to catch and handle all errors. It recovers a
|
|
// panic as well and prints its call stack. This is preferred helper for go
|
|
// workers on long running servers.
|
|
func CatchTrace(errorHandler func(err error)) {
|
|
// This and Handle are similar but we need to call recover here because how
|
|
// it works with defer. We cannot refactor these 2 to use same function.
|
|
|
|
if r := recover(); r != nil {
|
|
e, ok := r.(error)
|
|
if ok {
|
|
errorHandler(e)
|
|
} else {
|
|
println(r)
|
|
debug.PrintStack()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return is same as Handle but it's for functions which don't wrap or annotate
|
|
// their errors. If you want to annotate errors see Annotate for more
|
|
// information.
|
|
func Return(err *error) {
|
|
// This and Handle are similar but we need to call recover here because how
|
|
// it works with defer. We cannot refactor these two to use same function.
|
|
|
|
if r := recover(); r != nil {
|
|
e, ok := r.(error)
|
|
if !ok {
|
|
panic(r) // Not ours, carry on panicking
|
|
}
|
|
*err = e
|
|
}
|
|
}
|
|
|
|
// Annotate is for annotating an error. It's similar to Returnf but it takes only
|
|
// two arguments: a prefix string and a pointer to error. It adds ": " between
|
|
// the prefix and the error text automatically.
|
|
func Annotate(prefix string, err *error) {
|
|
// This and Handle are similar but we need to call recover here because how
|
|
// it works with defer. We cannot refactor these two to use same function.
|
|
|
|
if r := recover(); r != nil {
|
|
e, ok := r.(error)
|
|
if !ok {
|
|
panic(r) // Not ours, carry on panicking
|
|
}
|
|
*err = e
|
|
format := prefix + ": " + e.Error()
|
|
*err = errors.New(format)
|
|
} else if *err != nil { // if other handlers call recovery() we still..
|
|
format := prefix + ": " + (*err).Error()
|
|
*err = errors.New(format)
|
|
}
|
|
}
|