add error code feature for package gerror

This commit is contained in:
John Guo
2020-12-10 23:33:24 +08:00
parent a577605726
commit 84fef8dea3
11 changed files with 188 additions and 50 deletions

View File

@ -14,73 +14,71 @@ import (
"fmt"
)
// ApiStack is the interface for Stack feature.
type ApiStack interface {
// apiCode is the interface for Code feature.
type apiCode interface {
Error() string // It should be an error.
Code() int
}
// apiStack is the interface for Stack feature.
type apiStack interface {
Error() string // It should be an error.
Stack() string
}
// ApiCause is the interface for Cause feature.
type ApiCause interface {
// apiCause is the interface for Cause feature.
type apiCause interface {
Error() string // It should be an error.
Cause() error
}
// ApiCurrent is the interface for Current feature.
type ApiCurrent interface {
// apiCurrent is the interface for Current feature.
type apiCurrent interface {
Error() string // It should be an error.
Current() error
}
// ApiNext is the interface for Next feature.
type ApiNext interface {
// apiNext is the interface for Next feature.
type apiNext interface {
Error() string // It should be an error.
Next() error
}
// New creates and returns an error which is formatted from given text.
func New(text string) error {
if text == "" {
return nil
}
return &Error{
stack: callers(),
text: text,
code: -1,
}
}
// Newf returns an error that formats as the given format and args.
func Newf(format string, args ...interface{}) error {
return &Error{
stack: callers(),
text: fmt.Sprintf(format, args...),
code: -1,
}
}
// NewSkip creates and returns an error which is formatted from given text.
// The parameter <skip> specifies the stack callers skipped amount.
func NewSkip(skip int, text string) error {
if text == "" {
return nil
}
return &Error{
stack: callers(skip),
text: text,
code: -1,
}
}
// Newf returns an error that formats as the given format and args.
func Newf(format string, args ...interface{}) error {
if format == "" {
return nil
}
return &Error{
stack: callers(),
text: fmt.Sprintf(format, args...),
}
}
// NewfSkip returns an error that formats as the given format and args.
// NewSkipf returns an error that formats as the given format and args.
// The parameter <skip> specifies the stack callers skipped amount.
func NewfSkip(skip int, format string, args ...interface{}) error {
if format == "" {
return nil
}
func NewSkipf(skip int, format string, args ...interface{}) error {
return &Error{
stack: callers(skip),
text: fmt.Sprintf(format, args...),
code: -1,
}
}
@ -94,6 +92,7 @@ func Wrap(err error, text string) error {
error: err,
stack: callers(),
text: text,
code: -1,
}
}
@ -108,13 +107,91 @@ func Wrapf(err error, format string, args ...interface{}) error {
error: err,
stack: callers(),
text: fmt.Sprintf(format, args...),
code: -1,
}
}
// NewCode creates and returns an error that has error code and given text.
func NewCode(code int, text string) error {
return &Error{
stack: callers(),
text: text,
code: code,
}
}
// NewCodef returns an error that has error code and formats as the given format and args.
func NewCodef(code int, format string, args ...interface{}) error {
return &Error{
stack: callers(),
text: fmt.Sprintf(format, args...),
code: code,
}
}
// NewCodeSkip creates and returns an error which has error code and is formatted from given text.
// The parameter <skip> specifies the stack callers skipped amount.
func NewCodeSkip(code, skip int, text string) error {
return &Error{
stack: callers(skip),
text: text,
code: code,
}
}
// NewCodeSkipf returns an error that has error code and formats as the given format and args.
// The parameter <skip> specifies the stack callers skipped amount.
func NewCodeSkipf(code, skip int, format string, args ...interface{}) error {
return &Error{
stack: callers(skip),
text: fmt.Sprintf(format, args...),
code: code,
}
}
// WrapCode wraps error with code and text.
// It returns nil if given err is nil.
func WrapCode(code int, err error, text string) error {
if err == nil {
return nil
}
return &Error{
error: err,
stack: callers(),
text: text,
code: code,
}
}
// WrapCodef wraps error with code and format specifier.
// It returns nil if given <err> is nil.
func WrapCodef(code int, err error, format string, args ...interface{}) error {
if err == nil {
return nil
}
return &Error{
error: err,
stack: callers(),
text: fmt.Sprintf(format, args...),
code: code,
}
}
// Cause returns the error code of current error.
// It returns -1 if it has no error code or it does not implements interface Code.
func Code(err error) int {
if err != nil {
if e, ok := err.(apiCode); ok {
return e.Code()
}
}
return -1
}
// Cause returns the root cause error of <err>.
func Cause(err error) error {
if err != nil {
if e, ok := err.(ApiCause); ok {
if e, ok := err.(apiCause); ok {
return e.Cause()
}
}
@ -127,7 +204,7 @@ func Stack(err error) string {
if err == nil {
return ""
}
if e, ok := err.(ApiStack); ok {
if e, ok := err.(apiStack); ok {
return e.Stack()
}
return ""
@ -139,7 +216,7 @@ func Current(err error) error {
if err == nil {
return nil
}
if e, ok := err.(ApiCurrent); ok {
if e, ok := err.(apiCurrent); ok {
return e.Current()
}
return err
@ -151,7 +228,7 @@ func Next(err error) error {
if err == nil {
return nil
}
if e, ok := err.(ApiNext); ok {
if e, ok := err.(apiNext); ok {
return e.Next()
}
return nil

View File

@ -1,4 +1,4 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -20,10 +20,11 @@ type Error struct {
error error // Wrapped error.
stack stack // Stack array, which records the stack information when this error is created or wrapped.
text string // Error text, which is created by New* functions.
code int // Error code if necessary.
}
const (
gFILTER_KEY = "/errors/gerror/gerror"
stackFilterKey = "/errors/gerror/gerror"
)
var (
@ -49,7 +50,19 @@ func (err *Error) Error() string {
}
return err.text
}
return err.error.Error()
if err.error != nil {
err.error.Error()
}
return ""
}
// Code returns the error code.
// It returns -1 if it has no error code.
func (err *Error) Code() int {
if err == nil {
return -1
}
return err.code
}
// Cause returns the root cause error.
@ -63,7 +76,7 @@ func (err *Error) Cause() error {
if e, ok := loop.error.(*Error); ok {
// Internal Error struct.
loop = e
} else if e, ok := loop.error.(ApiCause); ok {
} else if e, ok := loop.error.(apiCause); ok {
// Other Error that implements ApiCause interface.
return e.Cause()
} else {
@ -165,7 +178,7 @@ func formatSubStack(st stack, buffer *bytes.Buffer) {
for _, p := range st {
if fn := runtime.FuncForPC(p - 1); fn != nil {
file, line := fn.FileLine(p - 1)
if strings.Contains(file, gFILTER_KEY) {
if strings.Contains(file, stackFilterKey) {
continue
}
// Avoid stack string like "<autogenerated>"

View File

@ -1,4 +1,4 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -12,13 +12,13 @@ import "runtime"
type stack []uintptr
const (
gMAX_STACK_DEPTH = 32
maxStackDepth = 32
)
// callers returns the stack callers.
func callers(skip ...int) stack {
var (
pcs [gMAX_STACK_DEPTH]uintptr
pcs [maxStackDepth]uintptr
n = 3
)
if len(skip) > 0 {

View File

@ -1,4 +1,4 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -155,3 +155,45 @@ func Test_Next(t *testing.T) {
t.Assert(err, nil)
})
}
func Test_Code(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
err := errors.New("123")
t.Assert(gerror.Code(err), -1)
t.Assert(err.Error(), "123")
})
gtest.C(t, func(t *gtest.T) {
err := gerror.NewCode(1, "123")
t.Assert(gerror.Code(err), 1)
t.Assert(err.Error(), "123")
})
gtest.C(t, func(t *gtest.T) {
err := gerror.NewCodef(1, "%s", "123")
t.Assert(gerror.Code(err), 1)
t.Assert(err.Error(), "123")
})
gtest.C(t, func(t *gtest.T) {
err := gerror.NewCodeSkip(1, 0, "123")
t.Assert(gerror.Code(err), 1)
t.Assert(err.Error(), "123")
})
gtest.C(t, func(t *gtest.T) {
err := gerror.NewCodeSkipf(1, 0, "%s", "123")
t.Assert(gerror.Code(err), 1)
t.Assert(err.Error(), "123")
})
gtest.C(t, func(t *gtest.T) {
err := errors.New("1")
err = gerror.Wrap(err, "2")
err = gerror.WrapCode(1, err, "3")
t.Assert(gerror.Code(err), 1)
t.Assert(err.Error(), "3: 2: 1")
})
gtest.C(t, func(t *gtest.T) {
err := errors.New("1")
err = gerror.Wrap(err, "2")
err = gerror.WrapCodef(1, err, "%s", "3")
t.Assert(gerror.Code(err), 1)
t.Assert(err.Error(), "3: 2: 1")
})
}