Skip to content

Commit

Permalink
reduce dependencies down to stackfmt
Browse files Browse the repository at this point in the history
  • Loading branch information
gregwebs committed Feb 4, 2025
1 parent e40ba61 commit 5bfd767
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 36 deletions.
6 changes: 2 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
module github.com/gregwebs/go-recovery

go 1.21

toolchain go1.21.5
go 1.23.3

require (
github.com/gregwebs/errors v1.2.2
github.com/gregwebs/stackfmt v0.1.1
github.com/stretchr/testify v1.8.1
)

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gregwebs/errors v1.2.2 h1:JQG47s42qKzMPdIT8pmCkZ+2Trhoyskgimxbyp7lD+U=
github.com/gregwebs/errors v1.2.2/go.mod h1:1NkCObP7+scylHlC69lwHl2ACOHwktWYrZV4EJDEl6g=
github.com/gregwebs/stackfmt v0.1.1 h1:bqHZv69OGARsyNPD4tJfkJnu5oWjA86dISQKKfFt98A=
github.com/gregwebs/stackfmt v0.1.1/go.mod h1:MQWvus3s9juULflTEiFIllieKiz7AnX7i4HWyPOs2nQ=
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=
Expand Down
68 changes: 55 additions & 13 deletions recovery.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
package recovery

import (
"errors"
"fmt"
"io"
"log"

"github.com/gregwebs/errors"
"github.com/gregwebs/stackfmt"
)

// A Panic that was converted to an error.
type PanicError struct {
Panic interface{}
Stack stackfmt.Stack
}

func newPanicError(r interface{}, skip int) error {
return errors.AddStackSkip(PanicError{Panic: r}, skip)
return PanicError{Panic: r, Stack: stackfmt.NewStackSkip(skip)}
}

func (p PanicError) Unwrap() error {
Expand All @@ -32,36 +34,54 @@ func (p PanicError) Error() string {
}
}

func (p PanicError) HasStack() bool {
return true
}

func (p PanicError) StackTrace() stackfmt.StackTrace {
return p.Stack.StackTrace()
}

func (p PanicError) StackTraceFormat(s fmt.State, v rune) {
p.Stack.FormatStackTrace(s, v)
}

// This works with the extended syntax "%+v" for printing stack traces
func (p PanicError) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
if _, errWrite := fmt.Fprint(s, "panic: "); errWrite != nil {
errors.HandleWriteError(errWrite)
handleWriteError(errWrite)
}
p := p.Panic
if f, ok := p.(fmt.Formatter); ok {
if f, ok := p.Panic.(fmt.Formatter); ok {
f.Format(s, verb)
} else {
if _, errWrite := fmt.Fprintf(s, "%+v\n", p); errWrite != nil {
errors.HandleWriteError(errWrite)
if _, errWrite := fmt.Fprintf(s, "%+v", p.Panic); errWrite != nil {
handleWriteError(errWrite)
}
}
if p.Stack != nil {
if _, errWrite := io.WriteString(s, "\n"); errWrite != nil {
handleWriteError(errWrite)
}
p.Stack.FormatStackTrace(s, verb)
}
return
}
fallthrough
case 's', 'q':
if _, errWrite := io.WriteString(s, p.Error()); errWrite != nil {
errors.HandleWriteError(errWrite)
handleWriteError(errWrite)
}
}
}

// An error that was intentionally thrown via panic
// Pass it through without wrapping it as a PanicError
type ThrownError struct {
Err error
Err error
Stack stackfmt.Stack
}

func (e ThrownError) Unwrap() error {
Expand All @@ -72,6 +92,18 @@ func (e ThrownError) Error() string {
return e.Unwrap().Error()
}

func (e ThrownError) HasStack() bool {
return true
}

func (e ThrownError) StackTrace() stackfmt.StackTrace {
return e.Stack.StackTrace()
}

func (e ThrownError) StackTraceFormat(s fmt.State, v rune) {
e.Stack.FormatStackTrace(s, v)
}

// Call is a helper function which allows you to easily recover from panics in the given function parameter "fn".
// If fn returns an error, that will be returned.
// If a panic occurs, Call will convert it to a PanicError and return it.
Expand Down Expand Up @@ -170,12 +202,12 @@ func ToError(r interface{}) error {

// Unwrap a ThrownError
case ThrownError:
return r.Unwrap()
return r
case *ThrownError:
if r == nil {
return nil
}
return r.Unwrap()
return *r

// return a PanicError as is
case PanicError:
Expand Down Expand Up @@ -207,10 +239,20 @@ func ToError(r interface{}) error {
// }
// }
func Throw(err error) {
panic(ThrownError{Err: errors.AddStack(err)})
panic(ThrownError{Err: err, Stack: stackfmt.NewStackSkip(1)})
}

// A convenience function for calling Throw(fmt.Errorf(...))
func Throwf(format string, args ...interface{}) {
panic(ThrownError{Err: errors.Errorf(format, args...)})
panic(ThrownError{Err: fmt.Errorf(format, args...), Stack: stackfmt.NewStackSkip(1)})
}

// HandleFmtWriteError handles (rare) errors when writing to fmt.State.
// It defaults to printing the errors.
func HandleFmtWriteError(handler func(err error)) {
handleWriteError = handler
}

var handleWriteError = func(err error) {
log.Println(err)
}
36 changes: 19 additions & 17 deletions recovery_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package recovery_test

import (
"errors"
"fmt"
"testing"

"github.com/gregwebs/errors"
"github.com/gregwebs/go-recovery"
"github.com/stretchr/testify/assert"
)
Expand All @@ -15,23 +15,24 @@ func TestCallNil(t *testing.T) {
return nil
})
assert.Nil(t, err)
assert.False(t, errors.HasStack(err))
}

func HasStack(err error) bool {
if errWithStack, ok := err.(interface{ HasStack() bool }); ok {
return errWithStack.HasStack()
}
return false
}

func TestCallError(t *testing.T) {
var errOrig error
// return a basic error
err := recovery.Call(func() error {

Check failure on line 30 in recovery_test.go

View workflow job for this annotation

GitHub Actions / lint

undefined: recovery (typecheck)
return fmt.Errorf("return error")
})
assert.NotNil(t, err)
assert.False(t, errors.HasStack(err))

// return an error with stack trace
err = recovery.Call(func() error {
return errors.Errorf("return error with stack")
errOrig = fmt.Errorf("return error")
return errOrig
})
assert.NotNil(t, err)
assert.True(t, errors.HasStack(err))
assert.Equal(t, errOrig, err)
}

func TestCallPanicValue(t *testing.T) {
Expand All @@ -40,15 +41,15 @@ func TestCallPanicValue(t *testing.T) {
panic("panic")
})
assert.NotNil(t, err)
assert.True(t, errors.HasStack(err))
assert.True(t, HasStack(err))
assert.Equal(t, "panic: panic", err.Error())

// panic nil
err = recovery.Call(func() error {
panic(nil)
})
assert.NotNil(t, err)
assert.True(t, errors.HasStack(err))
assert.True(t, HasStack(err))
assert.Equal(t, "panic: panic called with nil argument", err.Error())
}

Expand All @@ -60,8 +61,8 @@ func TestCallPanicError(t *testing.T) {
panic(standardErr)
})

Check failure on line 62 in recovery_test.go

View workflow job for this annotation

GitHub Actions / lint

missing return (typecheck)
assert.NotNil(t, err)
assert.IsType(t, errors.AddStack(standardErr), err)
assert.True(t, errors.HasStack(err))
assert.IsType(t, recovery.PanicError{}, err)
assert.True(t, HasStack(err))
assert.Equal(t, "panic: error standard", err.Error())

// panic error
Expand All @@ -70,7 +71,7 @@ func TestCallPanicError(t *testing.T) {
})

Check failure on line 71 in recovery_test.go

View workflow job for this annotation

GitHub Actions / lint

missing return (typecheck)
assert.IsType(t, recovery.PanicError{}, err)
assert.NotNil(t, err)
assert.True(t, errors.HasStack(err))
assert.True(t, HasStack(err))
assert.Equal(t, "panic: error with stack", err.Error())
fullPrint := fmt.Sprintf("%+v", err)
assert.Contains(t, fullPrint, "recovery_test.go")
Expand All @@ -96,7 +97,8 @@ func TestCallThrown(t *testing.T) {
panic("panic")
})
assert.NotNil(t, err)
assert.Equal(t, recovery.PanicError{Panic: "panic"}, errors.Unwrap(err))
assert.IsType(t, recovery.PanicError{}, err)
assert.Equal(t, "panic: panic", err.Error())
}

func TestGoHandler(t *testing.T) {
Expand Down

0 comments on commit 5bfd767

Please sign in to comment.