From 79591889a97af09f177601e3e9e61f9c7e359359 Mon Sep 17 00:00:00 2001 From: mh-cbon Date: Sat, 5 Nov 2016 10:27:46 +0100 Subject: [PATCH] Close #558: detect FormattedError and print their stack trace --- errors.go | 14 ++++++++++---- errors_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/errors.go b/errors.go index ddef369..e0f295e 100644 --- a/errors.go +++ b/errors.go @@ -34,6 +34,10 @@ func (m MultiError) Error() string { return strings.Join(errs, "\n") } +type ErrorFormatter interface { + Format(s fmt.State, verb rune) +} + // ExitCoder is the interface checked by `App` and `Command` for a custom exit // code type ExitCoder interface { @@ -44,11 +48,11 @@ type ExitCoder interface { // ExitError fulfills both the builtin `error` interface and `ExitCoder` type ExitError struct { exitCode int - message string + message interface{} } // NewExitError makes a new *ExitError -func NewExitError(message string, exitCode int) *ExitError { +func NewExitError(message interface{}, exitCode int) *ExitError { return &ExitError{ exitCode: exitCode, message: message, @@ -58,7 +62,7 @@ func NewExitError(message string, exitCode int) *ExitError { // Error returns the string message, fulfilling the interface required by // `error` func (ee *ExitError) Error() string { - return ee.message + return fmt.Sprintf("%v", ee.message) } // ExitCode returns the exit code, fulfilling the interface required by @@ -77,7 +81,9 @@ func HandleExitCoder(err error) { } if exitErr, ok := err.(ExitCoder); ok { - if err.Error() != "" { + if _, ok := exitErr.(ErrorFormatter); ok { + fmt.Fprintf(ErrWriter, "%+v\n", err) + } else { fmt.Fprintln(ErrWriter, err) } OsExiter(exitErr.ExitCode()) diff --git a/errors_test.go b/errors_test.go index 04df031..e6dfc16 100644 --- a/errors_test.go +++ b/errors_test.go @@ -3,6 +3,7 @@ package cli import ( "bytes" "errors" + "fmt" "testing" ) @@ -104,3 +105,32 @@ func TestHandleExitCoder_ErrorWithoutMessage(t *testing.T) { expect(t, called, true) expect(t, ErrWriter.(*bytes.Buffer).String(), "") } + +// make a stub to not import pkg/errors +type ErrorWithFormat struct { + error +} + +func (f *ErrorWithFormat) Format(s fmt.State, verb rune) { + fmt.Fprintf(s, "This the format: %v", f.error) +} + +func TestHandleExitCoder_ErrorWithFormat(t *testing.T) { + called := false + + OsExiter = func(rc int) { + called = true + } + ErrWriter = &bytes.Buffer{} + + defer func() { + OsExiter = fakeOsExiter + ErrWriter = fakeErrWriter + }() + + err := &ErrorWithFormat{error: errors.New("I am formatted")} + HandleExitCoder(err) + + expect(t, called, true) + expect(t, ErrWriter.(*bytes.Buffer).String(), "This the format: I am formatted\n") +}