diff --git a/errors/gerror/gerror.go b/errors/gerror/gerror.go index d62d41380..170ecc5bb 100644 --- a/errors/gerror/gerror.go +++ b/errors/gerror/gerror.go @@ -26,6 +26,12 @@ type ApiCause interface { Cause() error } +// ApiLevel is the interface for Current/Next feature. +type ApiLevel interface { + Current() error + Next() error +} + // New creates and returns an error which is formatted from given text. func New(text string) error { if text == "" { @@ -120,3 +126,27 @@ func Stack(err error) string { } return "" } + +// Current creates and returns the current level error. +// It returns nil if current level error is nil. +func Current(err error) error { + if err == nil { + return nil + } + if e, ok := err.(ApiLevel); ok { + return e.Current() + } + return nil +} + +// Next returns the next level error. +// It returns nil if current level error or the next level error is nil. +func Next(err error) error { + if err == nil { + return nil + } + if e, ok := err.(ApiLevel); ok { + return e.Next() + } + return nil +} diff --git a/errors/gerror/gerror_error.go b/errors/gerror/gerror_error.go index b0cf7b539..1e8197ef4 100644 --- a/errors/gerror/gerror_error.go +++ b/errors/gerror/gerror_error.go @@ -27,6 +27,7 @@ const ( var ( // goRootForFilter is used for stack filtering purpose. + // Mainly for development environment. goRootForFilter = runtime.GOROOT() ) @@ -36,8 +37,11 @@ func init() { } } -// Error implements the interface of Error, it returns the error as string. +// Error implements the interface of Error, it returns all the error as string. func (err *Error) Error() string { + if err == nil { + return "" + } if err.text != "" { if err.error != nil { return err.text + ": " + err.error.Error() @@ -49,6 +53,9 @@ func (err *Error) Error() string { // Cause returns the root cause error. func (err *Error) Cause() error { + if err == nil { + return nil + } loop := err for loop != nil { if loop.error != nil { @@ -66,8 +73,8 @@ func (err *Error) Cause() error { // Format formats the frame according to the fmt.Formatter interface. // -// %v, %s : Print the error string; -// %-v, %-s : Print current error string; +// %v, %s : Print all the error string; +// %-v, %-s : Print current level error string; // %+s : Print full stack error list; // %+v : Print the error string and full stack error list; func (err *Error) Format(s fmt.State, verb rune) { @@ -120,6 +127,28 @@ func (err *Error) Stack() string { return buffer.String() } +// Current creates and returns the current level error. +// It returns nil if current level error is nil. +func (err *Error) Current() error { + if err == nil { + return nil + } + return &Error{ + error: nil, + stack: err.stack, + text: err.text, + } +} + +// Next returns the next level error. +// It returns nil if current level error or the next level error is nil. +func (err *Error) Next() error { + if err == nil { + return nil + } + return err.error +} + // formatSubStack formats the stack for error. func formatSubStack(st stack, buffer *bytes.Buffer) { index := 1 diff --git a/errors/gerror/gerror_test.go b/errors/gerror/gerror_test.go index 9b7346ce9..e1f64a92e 100644 --- a/errors/gerror/gerror_test.go +++ b/errors/gerror/gerror_test.go @@ -127,3 +127,31 @@ func Test_Stack(t *testing.T) { //fmt.Printf("%+v", err) }) } + +func Test_Current(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + err := errors.New("1") + err = gerror.Wrap(err, "2") + err = gerror.Wrap(err, "3") + t.Assert(err.Error(), "3: 2: 1") + t.Assert(gerror.Current(err).Error(), "3") + }) +} + +func Test_Next(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + err := errors.New("1") + err = gerror.Wrap(err, "2") + err = gerror.Wrap(err, "3") + t.Assert(err.Error(), "3: 2: 1") + + err = gerror.Next(err) + t.Assert(err.Error(), "2: 1") + + err = gerror.Next(err) + t.Assert(err.Error(), "1") + + err = gerror.Next(err) + t.Assert(err, nil) + }) +}