mirror of
https://github.com/gogf/gf.git
synced 2025-04-05 03:05:05 +08:00
add Is/Equal/Unwrap functions for package gerror
This commit is contained in:
parent
2d6fcf5d06
commit
e8581d4fd5
@ -17,36 +17,6 @@ import (
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
)
|
||||
|
||||
// iCode is the interface for Code feature.
|
||||
type iCode interface {
|
||||
Error() string
|
||||
Code() gcode.Code
|
||||
}
|
||||
|
||||
// iStack is the interface for Stack feature.
|
||||
type iStack interface {
|
||||
Error() string
|
||||
Stack() string
|
||||
}
|
||||
|
||||
// iCause is the interface for Cause feature.
|
||||
type iCause interface {
|
||||
Error() string
|
||||
Cause() error
|
||||
}
|
||||
|
||||
// iCurrent is the interface for Current feature.
|
||||
type iCurrent interface {
|
||||
Error() string
|
||||
Current() error
|
||||
}
|
||||
|
||||
// iNext is the interface for Next feature.
|
||||
type iNext interface {
|
||||
Error() string
|
||||
Next() error
|
||||
}
|
||||
|
||||
// New creates and returns an error which is formatted from given text.
|
||||
func New(text string) error {
|
||||
return &Error{
|
||||
@ -305,8 +275,39 @@ func Next(err error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unwrap is alias of function `Next`.
|
||||
// It is just for implements for stdlib errors.Unwrap from Go version 1.17.
|
||||
func Unwrap(err error) error {
|
||||
return Next(err)
|
||||
}
|
||||
|
||||
// HasStack checks and returns whether `err` implemented interface `iStack`.
|
||||
func HasStack(err error) bool {
|
||||
_, ok := err.(iStack)
|
||||
return ok
|
||||
}
|
||||
|
||||
// Equal reports whether current error `err` equals to error `target`.
|
||||
// Please note that, in default comparison for `Error`,
|
||||
// the errors are considered the same if both the `code` and `text` of them are the same.
|
||||
func Equal(err, target error) bool {
|
||||
if err == target {
|
||||
return true
|
||||
}
|
||||
if e, ok := err.(iEqual); ok {
|
||||
return e.Equal(target)
|
||||
}
|
||||
if e, ok := target.(iEqual); ok {
|
||||
return e.Equal(err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Is reports whether current error `err` has error `target` in its chaining errors.
|
||||
// It is just for implements for stdlib errors.Unwrap from Go version 1.17.
|
||||
func Is(err, target error) bool {
|
||||
if e, ok := err.(iIs); ok {
|
||||
return e.Is(target)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -7,10 +7,8 @@
|
||||
package gerror
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
@ -59,18 +57,6 @@ func (err *Error) Error() string {
|
||||
return errStr
|
||||
}
|
||||
|
||||
// Code returns the error code.
|
||||
// It returns CodeNil if it has no error code.
|
||||
func (err *Error) Code() gcode.Code {
|
||||
if err == nil {
|
||||
return gcode.CodeNil
|
||||
}
|
||||
if err.code == gcode.CodeNil {
|
||||
return Code(err.Next())
|
||||
}
|
||||
return err.code
|
||||
}
|
||||
|
||||
// Cause returns the root cause error.
|
||||
func (err *Error) Cause() error {
|
||||
if err == nil {
|
||||
@ -97,64 +83,6 @@ func (err *Error) Cause() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Format formats the frame according to the fmt.Formatter interface.
|
||||
//
|
||||
// %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) {
|
||||
switch verb {
|
||||
case 's', 'v':
|
||||
switch {
|
||||
case s.Flag('-'):
|
||||
if err.text != "" {
|
||||
_, _ = io.WriteString(s, err.text)
|
||||
} else {
|
||||
_, _ = io.WriteString(s, err.Error())
|
||||
}
|
||||
case s.Flag('+'):
|
||||
if verb == 's' {
|
||||
_, _ = io.WriteString(s, err.Stack())
|
||||
} else {
|
||||
_, _ = io.WriteString(s, err.Error()+"\n"+err.Stack())
|
||||
}
|
||||
default:
|
||||
_, _ = io.WriteString(s, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stack returns the stack callers as string.
|
||||
// It returns an empty string if the `err` does not support stacks.
|
||||
func (err *Error) Stack() string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
var (
|
||||
loop = err
|
||||
index = 1
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
)
|
||||
for loop != nil {
|
||||
buffer.WriteString(fmt.Sprintf("%d. %-v\n", index, loop))
|
||||
index++
|
||||
formatSubStack(loop.stack, buffer)
|
||||
if loop.error != nil {
|
||||
if e, ok := loop.error.(*Error); ok {
|
||||
loop = e
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf("%d. %s\n", index, loop.error.Error()))
|
||||
index++
|
||||
break
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
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 {
|
||||
@ -178,53 +106,46 @@ func (err *Error) Next() error {
|
||||
return err.error
|
||||
}
|
||||
|
||||
// SetCode updates the internal code with given code.
|
||||
func (err *Error) SetCode(code gcode.Code) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
err.code = code
|
||||
// Unwrap is alias of function `Next`.
|
||||
// It is just for implements for stdlib errors.Unwrap from Go version 1.17.
|
||||
func (err *Error) Unwrap() error {
|
||||
return err.Next()
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (err Error) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"` + err.Error() + `"`), nil
|
||||
// Equal reports whether current error `err` equals to error `target`.
|
||||
// Please note that, in default comparison for `Error`,
|
||||
// the errors are considered the same if both the `code` and `text` of them are the same.
|
||||
func (err *Error) Equal(target error) bool {
|
||||
if err == target {
|
||||
return true
|
||||
}
|
||||
// Code should be the same.
|
||||
// Note that if both errors have `nil` code, they are also considered equal.
|
||||
if err.code != Code(target) {
|
||||
return false
|
||||
}
|
||||
// Text should be the same.
|
||||
if err.text != fmt.Sprintf(`%-s`, target) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// formatSubStack formats the stack for error.
|
||||
func formatSubStack(st stack, buffer *bytes.Buffer) {
|
||||
if st == nil {
|
||||
return
|
||||
// Is reports whether current error `err` has error `target` in its chaining errors.
|
||||
// It is just for implements for stdlib errors.Unwrap from Go version 1.17.
|
||||
func (err *Error) Is(target error) bool {
|
||||
if Equal(err, target) {
|
||||
return true
|
||||
}
|
||||
index := 1
|
||||
space := " "
|
||||
for _, p := range st {
|
||||
if fn := runtime.FuncForPC(p - 1); fn != nil {
|
||||
file, line := fn.FileLine(p - 1)
|
||||
// Custom filtering.
|
||||
if strings.Contains(file, stackFilterKeyLocal) {
|
||||
continue
|
||||
}
|
||||
// Avoid stack string like "`autogenerated`"
|
||||
if strings.Contains(file, "<") {
|
||||
continue
|
||||
}
|
||||
// Ignore GO ROOT paths.
|
||||
if goRootForFilter != "" &&
|
||||
len(file) >= len(goRootForFilter) &&
|
||||
file[0:len(goRootForFilter)] == goRootForFilter {
|
||||
continue
|
||||
}
|
||||
// Graceful indent.
|
||||
if index > 9 {
|
||||
space = " "
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf(
|
||||
" %d).%s%s\n \t%s:%d\n",
|
||||
index, space, fn.Name(), file, line,
|
||||
))
|
||||
index++
|
||||
}
|
||||
nextErr := err.Next()
|
||||
if nextErr == nil {
|
||||
return false
|
||||
}
|
||||
if Equal(nextErr, target) {
|
||||
return true
|
||||
}
|
||||
if e, ok := nextErr.(iIs); ok {
|
||||
return e.Is(target)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
31
errors/gerror/gerror_error_code.go
Normal file
31
errors/gerror/gerror_error_code.go
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gerror
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
)
|
||||
|
||||
// Code returns the error code.
|
||||
// It returns CodeNil if it has no error code.
|
||||
func (err *Error) Code() gcode.Code {
|
||||
if err == nil {
|
||||
return gcode.CodeNil
|
||||
}
|
||||
if err.code == gcode.CodeNil {
|
||||
return Code(err.Next())
|
||||
}
|
||||
return err.code
|
||||
}
|
||||
|
||||
// SetCode updates the internal code with given code.
|
||||
func (err *Error) SetCode(code gcode.Code) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
err.code = code
|
||||
}
|
80
errors/gerror/gerror_error_format.go
Normal file
80
errors/gerror/gerror_error_format.go
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gerror
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Format formats the frame according to the fmt.Formatter interface.
|
||||
//
|
||||
// %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) {
|
||||
switch verb {
|
||||
case 's', 'v':
|
||||
switch {
|
||||
case s.Flag('-'):
|
||||
if err.text != "" {
|
||||
_, _ = io.WriteString(s, err.text)
|
||||
} else {
|
||||
_, _ = io.WriteString(s, err.Error())
|
||||
}
|
||||
case s.Flag('+'):
|
||||
if verb == 's' {
|
||||
_, _ = io.WriteString(s, err.Stack())
|
||||
} else {
|
||||
_, _ = io.WriteString(s, err.Error()+"\n"+err.Stack())
|
||||
}
|
||||
default:
|
||||
_, _ = io.WriteString(s, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// formatSubStack formats the stack for error.
|
||||
func formatSubStack(st stack, buffer *bytes.Buffer) {
|
||||
if st == nil {
|
||||
return
|
||||
}
|
||||
index := 1
|
||||
space := " "
|
||||
for _, p := range st {
|
||||
if fn := runtime.FuncForPC(p - 1); fn != nil {
|
||||
file, line := fn.FileLine(p - 1)
|
||||
// Custom filtering.
|
||||
if strings.Contains(file, stackFilterKeyLocal) {
|
||||
continue
|
||||
}
|
||||
// Avoid stack string like "`autogenerated`"
|
||||
if strings.Contains(file, "<") {
|
||||
continue
|
||||
}
|
||||
// Ignore GO ROOT paths.
|
||||
if goRootForFilter != "" &&
|
||||
len(file) >= len(goRootForFilter) &&
|
||||
file[0:len(goRootForFilter)] == goRootForFilter {
|
||||
continue
|
||||
}
|
||||
// Graceful indent.
|
||||
if index > 9 {
|
||||
space = " "
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf(
|
||||
" %d).%s%s\n \t%s:%d\n",
|
||||
index, space, fn.Name(), file, line,
|
||||
))
|
||||
index++
|
||||
}
|
||||
}
|
||||
}
|
13
errors/gerror/gerror_error_json.go
Normal file
13
errors/gerror/gerror_error_json.go
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gerror
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (err Error) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"` + err.Error() + `"`), nil
|
||||
}
|
42
errors/gerror/gerror_error_stack.go
Normal file
42
errors/gerror/gerror_error_stack.go
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gerror
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Stack returns the stack callers as string.
|
||||
// It returns an empty string if the `err` does not support stacks.
|
||||
func (err *Error) Stack() string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
var (
|
||||
loop = err
|
||||
index = 1
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
)
|
||||
for loop != nil {
|
||||
buffer.WriteString(fmt.Sprintf("%d. %-v\n", index, loop))
|
||||
index++
|
||||
formatSubStack(loop.stack, buffer)
|
||||
if loop.error != nil {
|
||||
if e, ok := loop.error.(*Error); ok {
|
||||
loop = e
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf("%d. %s\n", index, loop.error.Error()))
|
||||
index++
|
||||
break
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
51
errors/gerror/gerror_interface.go
Normal file
51
errors/gerror/gerror_interface.go
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gerror
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
)
|
||||
|
||||
// iIs is the interface for Is feature.
|
||||
type iIs interface {
|
||||
Is(target error) bool
|
||||
}
|
||||
|
||||
// iEqual is the interface for Equal feature.
|
||||
type iEqual interface {
|
||||
Equal(target error) bool
|
||||
}
|
||||
|
||||
// iCode is the interface for Code feature.
|
||||
type iCode interface {
|
||||
Error() string
|
||||
Code() gcode.Code
|
||||
}
|
||||
|
||||
// iStack is the interface for Stack feature.
|
||||
type iStack interface {
|
||||
Error() string
|
||||
Stack() string
|
||||
}
|
||||
|
||||
// iCause is the interface for Cause feature.
|
||||
type iCause interface {
|
||||
Error() string
|
||||
Cause() error
|
||||
}
|
||||
|
||||
// iCurrent is the interface for Current feature.
|
||||
type iCurrent interface {
|
||||
Error() string
|
||||
Current() error
|
||||
}
|
||||
|
||||
// iNext is the interface for Next feature.
|
||||
type iNext interface {
|
||||
Error() string
|
||||
Next() error
|
||||
}
|
@ -16,7 +16,7 @@ type Option struct {
|
||||
Code gcode.Code // Error code if necessary.
|
||||
}
|
||||
|
||||
// NewOption creates and returns an error with Option.
|
||||
// NewOption creates and returns a custom error with Option.
|
||||
// It is the senior usage for creating error, which is often used internally in framework.
|
||||
func NewOption(option Option) error {
|
||||
err := &Error{
|
||||
|
@ -255,6 +255,24 @@ func Test_Next(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Unwrap(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.Unwrap(err)
|
||||
t.Assert(err.Error(), "2: 1")
|
||||
|
||||
err = gerror.Unwrap(err)
|
||||
t.Assert(err.Error(), "1")
|
||||
|
||||
err = gerror.Unwrap(err)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Code(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
err := errors.New("123")
|
||||
@ -340,3 +358,26 @@ func Test_HasStack(t *testing.T) {
|
||||
t.Assert(gerror.HasStack(err2), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Equal(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
err1 := errors.New("1")
|
||||
err2 := errors.New("1")
|
||||
err3 := gerror.New("1")
|
||||
err4 := gerror.New("4")
|
||||
t.Assert(gerror.Equal(err1, err2), false)
|
||||
t.Assert(gerror.Equal(err1, err3), true)
|
||||
t.Assert(gerror.Equal(err2, err3), true)
|
||||
t.Assert(gerror.Equal(err3, err4), false)
|
||||
t.Assert(gerror.Equal(err1, err4), false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Is(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
err1 := errors.New("1")
|
||||
err2 := gerror.Wrap(err1, "2")
|
||||
err2 = gerror.Wrap(err2, "3")
|
||||
t.Assert(gerror.Is(err2, err1), true)
|
||||
})
|
||||
}
|
||||
|
@ -152,9 +152,10 @@ func (r *Request) Get(key string, def ...interface{}) *gvar.Var {
|
||||
func (r *Request) GetBody() []byte {
|
||||
if r.bodyContent == nil {
|
||||
var err error
|
||||
r.bodyContent, err = ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
panic(gerror.WrapCode(gcode.CodeInternalError, err, `ReadAll from body failed`))
|
||||
if r.bodyContent, err = ioutil.ReadAll(r.Body); err != nil {
|
||||
errMsg := `Read from request Body failed`
|
||||
errMsg += `, the Body might be closed or read manually from middleware/hook/other package previously`
|
||||
panic(gerror.WrapCode(gcode.CodeInternalError, err, errMsg))
|
||||
}
|
||||
r.Body = utils.NewReadCloser(r.bodyContent, true)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user