Initial QSfera import
This commit is contained in:
+221
@@ -0,0 +1,221 @@
|
||||
# ErrorX
|
||||
|
||||
`errorx` provide an enhanced error implements for go, allow with stacktraces and wrap another error.
|
||||
|
||||
## Install
|
||||
|
||||
```go
|
||||
go get github.com/gookit/goutil/errorx
|
||||
```
|
||||
|
||||
## Go docs
|
||||
|
||||
- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/errorx)
|
||||
|
||||
## Usage
|
||||
|
||||
### Create error with call stack info
|
||||
|
||||
- use the `errorx.New` instead `errors.New`
|
||||
|
||||
```go
|
||||
func doSomething() error {
|
||||
if false {
|
||||
// return errors.New("a error happen")
|
||||
return errorx.New("a error happen")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- use the `errorx.Newf` or `errorx.Errorf` instead `fmt.Errorf`
|
||||
|
||||
```go
|
||||
func doSomething() error {
|
||||
if false {
|
||||
// return fmt.Errorf("a error %s", "happen")
|
||||
return errorx.Newf("a error %s", "happen")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Wrap the previous error
|
||||
|
||||
used like this before:
|
||||
|
||||
```go
|
||||
if err := SomeFunc(); err != nil {
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
can be replaced with:
|
||||
|
||||
```go
|
||||
if err := SomeFunc(); err != nil {
|
||||
return errors.Stacked(err)
|
||||
}
|
||||
```
|
||||
|
||||
## Output details
|
||||
|
||||
error output details for use `errorx`
|
||||
|
||||
### Use errorx.New
|
||||
|
||||
`errorx` functions for new error:
|
||||
|
||||
```go
|
||||
func New(msg string) error
|
||||
func Newf(tpl string, vars ...interface{}) error
|
||||
func Errorf(tpl string, vars ...interface{}) error
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
```go
|
||||
err := errorx.New("the error message")
|
||||
|
||||
fmt.Println(err)
|
||||
// fmt.Printf("%v\n", err)
|
||||
// fmt.Printf("%#v\n", err)
|
||||
```
|
||||
|
||||
> from the test: `errorx_test.TestNew()`
|
||||
|
||||
**Output**:
|
||||
|
||||
```text
|
||||
the error message
|
||||
STACK:
|
||||
github.com/gookit/goutil/errorx_test.returnXErr()
|
||||
/Users/inhere/Workspace/godev/gookit/goutil/errorx/errorx_test.go:21
|
||||
github.com/gookit/goutil/errorx_test.returnXErrL2()
|
||||
/Users/inhere/Workspace/godev/gookit/goutil/errorx/errorx_test.go:25
|
||||
github.com/gookit/goutil/errorx_test.TestNew()
|
||||
/Users/inhere/Workspace/godev/gookit/goutil/errorx/errorx_test.go:29
|
||||
testing.tRunner()
|
||||
/usr/local/Cellar/go/1.18/libexec/src/testing/testing.go:1439
|
||||
runtime.goexit()
|
||||
/usr/local/Cellar/go/1.18/libexec/src/runtime/asm_amd64.s:1571
|
||||
```
|
||||
|
||||
### Use errorx.With
|
||||
|
||||
`errorx` functions for with another error:
|
||||
|
||||
```go
|
||||
func With(err error, msg string) error
|
||||
func Withf(err error, tpl string, vars ...interface{}) error
|
||||
```
|
||||
|
||||
With a go raw error:
|
||||
|
||||
```go
|
||||
err1 := returnErr("first error message")
|
||||
|
||||
err2 := errorx.With(err1, "second error message")
|
||||
fmt.Println(err2)
|
||||
```
|
||||
|
||||
> from the test: `errorx_test.TestWith_goerr()`
|
||||
|
||||
**Output**:
|
||||
|
||||
```text
|
||||
second error message
|
||||
STACK:
|
||||
github.com/gookit/goutil/errorx_test.TestWith_goerr()
|
||||
/Users/inhere/Workspace/godev/gookit/goutil/errorx/errorx_test.go:51
|
||||
testing.tRunner()
|
||||
/usr/local/Cellar/go/1.18/libexec/src/testing/testing.go:1439
|
||||
runtime.goexit()
|
||||
/usr/local/Cellar/go/1.18/libexec/src/runtime/asm_amd64.s:1571
|
||||
|
||||
Previous: first error message
|
||||
```
|
||||
|
||||
With a `errorx` error:
|
||||
|
||||
```go
|
||||
err1 := returnXErr("first error message")
|
||||
err2 := errorx.With(err1, "second error message")
|
||||
fmt.Println(err2)
|
||||
```
|
||||
|
||||
> from the test: `errorx_test.TestWith_errorx()`
|
||||
|
||||
**Output**:
|
||||
|
||||
```text
|
||||
second error message
|
||||
STACK:
|
||||
github.com/gookit/goutil/errorx_test.TestWith_errorx()
|
||||
/Users/inhere/Workspace/godev/gookit/goutil/errorx/errorx_test.go:64
|
||||
testing.tRunner()
|
||||
/usr/local/Cellar/go/1.18/libexec/src/testing/testing.go:1439
|
||||
runtime.goexit()
|
||||
/usr/local/Cellar/go/1.18/libexec/src/runtime/asm_amd64.s:1571
|
||||
|
||||
Previous: first error message
|
||||
STACK:
|
||||
github.com/gookit/goutil/errorx_test.returnXErr()
|
||||
/Users/inhere/Workspace/godev/gookit/goutil/errorx/errorx_test.go:21
|
||||
github.com/gookit/goutil/errorx_test.TestWith_errorx()
|
||||
/Users/inhere/Workspace/godev/gookit/goutil/errorx/errorx_test.go:61
|
||||
testing.tRunner()
|
||||
/usr/local/Cellar/go/1.18/libexec/src/testing/testing.go:1439
|
||||
runtime.goexit()
|
||||
/usr/local/Cellar/go/1.18/libexec/src/runtime/asm_amd64.s:1571
|
||||
|
||||
```
|
||||
|
||||
### Use errorx.Wrap
|
||||
|
||||
```go
|
||||
err := errors.New("first error message")
|
||||
err = errorx.Wrap(err, "second error message")
|
||||
err = errorx.Wrap(err, "third error message")
|
||||
// fmt.Println(err)
|
||||
// fmt.Println(err.Error())
|
||||
```
|
||||
|
||||
Direct print the `err`:
|
||||
|
||||
```text
|
||||
third error message
|
||||
Previous: second error message
|
||||
Previous: first error message
|
||||
```
|
||||
|
||||
Print the `err.Error()`:
|
||||
|
||||
```text
|
||||
third error message; second error message; first error message
|
||||
```
|
||||
|
||||
## Code Check & Testing
|
||||
|
||||
```bash
|
||||
gofmt -w -l ./
|
||||
golint ./...
|
||||
```
|
||||
|
||||
**Testing**:
|
||||
|
||||
```shell
|
||||
go test -v ./errorx/...
|
||||
```
|
||||
|
||||
**Test limit by regexp**:
|
||||
|
||||
```shell
|
||||
go test -v -run ^TestSetByKeys ./errorx/...
|
||||
```
|
||||
|
||||
## Refers
|
||||
|
||||
- golang errors
|
||||
- https://github.com/joomcode/errorx
|
||||
- https://github.com/pkg/errors
|
||||
- https://github.com/juju/errors
|
||||
- https://github.com/go-errors/errors
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package errorx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/gookit/goutil/arrutil"
|
||||
"github.com/gookit/goutil/comdef"
|
||||
"github.com/gookit/goutil/internal/comfunc"
|
||||
)
|
||||
|
||||
// IsTrue assert result is true, otherwise will return error
|
||||
func IsTrue(result bool, fmtAndArgs ...any) error {
|
||||
if !result {
|
||||
return errors.New(formatErrMsg("result should be True", fmtAndArgs))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsFalse assert result is false, otherwise will return error
|
||||
func IsFalse(result bool, fmtAndArgs ...any) error {
|
||||
if result {
|
||||
return errors.New(formatErrMsg("result should be False", fmtAndArgs))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsIn value should be in the list, otherwise will return error
|
||||
func IsIn[T comdef.ScalarType](value T, list []T, fmtAndArgs ...any) error {
|
||||
if arrutil.NotIn(value, list) {
|
||||
var errMsg string
|
||||
if len(fmtAndArgs) > 0 {
|
||||
errMsg = comfunc.FormatWithArgs(fmtAndArgs)
|
||||
} else {
|
||||
errMsg = fmt.Sprintf("value should be in the %v", list)
|
||||
}
|
||||
return errors.New(errMsg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NotIn value should not be in the list, otherwise will return error
|
||||
func NotIn[T comdef.ScalarType](value T, list []T, fmtAndArgs ...any) error {
|
||||
if arrutil.In(value, list) {
|
||||
var errMsg string
|
||||
if len(fmtAndArgs) > 0 {
|
||||
errMsg = comfunc.FormatWithArgs(fmtAndArgs)
|
||||
} else {
|
||||
errMsg = fmt.Sprintf("value should not be in the %v", list)
|
||||
}
|
||||
return errors.New(errMsg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatErrMsg(defMsg string, fmtAndArgs []any) string {
|
||||
if len(fmtAndArgs) > 0 {
|
||||
return comfunc.FormatWithArgs(fmtAndArgs)
|
||||
}
|
||||
return defMsg
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
package errorx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ErrorCoder interface
|
||||
type ErrorCoder interface {
|
||||
error
|
||||
Code() int
|
||||
}
|
||||
|
||||
// ErrorR useful for web service replay/response.
|
||||
// code == 0 is successful. otherwise, is failed.
|
||||
type ErrorR interface {
|
||||
ErrorCoder
|
||||
fmt.Stringer
|
||||
IsSuc() bool
|
||||
IsFail() bool
|
||||
}
|
||||
|
||||
// error reply struct
|
||||
type errorR struct {
|
||||
code int
|
||||
msg string
|
||||
}
|
||||
|
||||
// NewR code with error response
|
||||
func NewR(code int, msg string) ErrorR {
|
||||
return &errorR{code: code, msg: msg}
|
||||
}
|
||||
|
||||
// Fail code with error response
|
||||
func Fail(code int, msg string) ErrorR {
|
||||
return &errorR{code: code, msg: msg}
|
||||
}
|
||||
|
||||
// Failf code with error response
|
||||
func Failf(code int, tpl string, v ...any) ErrorR {
|
||||
return &errorR{code: code, msg: fmt.Sprintf(tpl, v...)}
|
||||
}
|
||||
|
||||
// Suc success response reply
|
||||
func Suc(msg string) ErrorR {
|
||||
return &errorR{code: 0, msg: msg}
|
||||
}
|
||||
|
||||
// IsSuc code value check
|
||||
func (e *errorR) IsSuc() bool {
|
||||
return e.code == 0
|
||||
}
|
||||
|
||||
// IsFail code value check
|
||||
func (e *errorR) IsFail() bool {
|
||||
return e.code != 0
|
||||
}
|
||||
|
||||
// Code value
|
||||
func (e *errorR) Code() int {
|
||||
return e.code
|
||||
}
|
||||
|
||||
// Error string
|
||||
func (e *errorR) Error() string {
|
||||
return e.msg
|
||||
}
|
||||
|
||||
// String get
|
||||
func (e *errorR) String() string {
|
||||
return e.msg + "(code: " + strconv.FormatInt(int64(e.code), 10) + ")"
|
||||
}
|
||||
|
||||
// GoString get.
|
||||
func (e *errorR) GoString() string {
|
||||
return e.String()
|
||||
}
|
||||
|
||||
// ErrorM multi error map
|
||||
type ErrorM map[string]error
|
||||
|
||||
// ErrMap alias of ErrorM
|
||||
type ErrMap = ErrorM
|
||||
|
||||
// Error string
|
||||
func (e ErrorM) Error() string {
|
||||
var sb strings.Builder
|
||||
for name, err := range e {
|
||||
sb.WriteString(name)
|
||||
sb.WriteByte(':')
|
||||
sb.WriteString(err.Error())
|
||||
sb.WriteByte('\n')
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// ErrorOrNil error
|
||||
func (e ErrorM) ErrorOrNil() error {
|
||||
if len(e) == 0 {
|
||||
return nil
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// IsEmpty error
|
||||
func (e ErrorM) IsEmpty() bool {
|
||||
return len(e) == 0
|
||||
}
|
||||
|
||||
// One error
|
||||
func (e ErrorM) One() error {
|
||||
for _, err := range e {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Errors multi error list
|
||||
type Errors []error
|
||||
|
||||
// ErrList alias for Errors
|
||||
type ErrList = Errors
|
||||
|
||||
// Error string
|
||||
func (es Errors) Error() string {
|
||||
var sb strings.Builder
|
||||
for _, err := range es {
|
||||
sb.WriteString(err.Error())
|
||||
sb.WriteByte('\n')
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// ErrorOrNil error
|
||||
func (es Errors) ErrorOrNil() error {
|
||||
if len(es) == 0 {
|
||||
return nil
|
||||
}
|
||||
return es
|
||||
}
|
||||
|
||||
// IsEmpty error
|
||||
func (es Errors) IsEmpty() bool {
|
||||
return len(es) == 0
|
||||
}
|
||||
|
||||
// First error
|
||||
func (es Errors) First() error {
|
||||
if len(es) > 0 {
|
||||
return es[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+336
@@ -0,0 +1,336 @@
|
||||
// Package errorx provide an enhanced error implements for go,
|
||||
// allow with stacktraces and wrap another error.
|
||||
package errorx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Causer interface for get first cause error
|
||||
type Causer interface {
|
||||
// Cause returns the first cause error by call err.Cause().
|
||||
// Otherwise, will returns current error.
|
||||
Cause() error
|
||||
}
|
||||
|
||||
// Unwrapper interface for get previous error
|
||||
type Unwrapper interface {
|
||||
// Unwrap returns previous error by call err.Unwrap().
|
||||
// Otherwise, will returns nil.
|
||||
Unwrap() error
|
||||
}
|
||||
|
||||
// XErrorFace interface
|
||||
type XErrorFace interface {
|
||||
error
|
||||
Causer
|
||||
Unwrapper
|
||||
}
|
||||
|
||||
// Exception interface
|
||||
// type Exception interface {
|
||||
// XErrorFace
|
||||
// Code() string
|
||||
// Message() string
|
||||
// StackString() string
|
||||
// }
|
||||
|
||||
/*************************************************************
|
||||
* implements XErrorFace interface
|
||||
*************************************************************/
|
||||
|
||||
// ErrorX struct
|
||||
//
|
||||
// TIPS:
|
||||
//
|
||||
// fmt pkg call order: Format > GoString > Error > String
|
||||
type ErrorX struct {
|
||||
// trace stack
|
||||
*stack
|
||||
prev error
|
||||
msg string
|
||||
}
|
||||
|
||||
// Cause implements Causer.
|
||||
func (e *ErrorX) Cause() error {
|
||||
if e.prev == nil {
|
||||
return e
|
||||
}
|
||||
|
||||
if ex, ok := e.prev.(*ErrorX); ok {
|
||||
return ex.Cause()
|
||||
}
|
||||
return e.prev
|
||||
}
|
||||
|
||||
// Unwrap implements Unwrapper.
|
||||
func (e *ErrorX) Unwrap() error {
|
||||
return e.prev
|
||||
}
|
||||
|
||||
// Format error, will output stack information.
|
||||
func (e *ErrorX) Format(s fmt.State, verb rune) {
|
||||
// format current error: only output on have msg
|
||||
if len(e.msg) > 0 {
|
||||
_, _ = io.WriteString(s, e.msg)
|
||||
if e.stack != nil {
|
||||
e.stack.Format(s, verb)
|
||||
}
|
||||
}
|
||||
|
||||
// format prev error
|
||||
if e.prev == nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = s.Write([]byte("\nPrevious: "))
|
||||
if ex, ok := e.prev.(*ErrorX); ok {
|
||||
ex.Format(s, verb)
|
||||
} else {
|
||||
_, _ = s.Write([]byte(e.prev.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
// GoString to GO string, contains stack information.
|
||||
// printing an error with %#v will produce useful information.
|
||||
func (e *ErrorX) GoString() string {
|
||||
// var sb strings.Builder
|
||||
var buf bytes.Buffer
|
||||
_, _ = e.WriteTo(&buf)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Error msg string, not contains stack information.
|
||||
func (e *ErrorX) Error() string {
|
||||
var buf bytes.Buffer
|
||||
e.writeMsgTo(&buf)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// String error to string, contains stack information.
|
||||
func (e *ErrorX) String() string {
|
||||
return e.GoString()
|
||||
}
|
||||
|
||||
// WriteTo write the error to a writer, contains stack information.
|
||||
func (e *ErrorX) WriteTo(w io.Writer) (n int64, err error) {
|
||||
// current error: only output on have msg
|
||||
if len(e.msg) > 0 {
|
||||
_, _ = w.Write([]byte(e.msg))
|
||||
|
||||
// with stack
|
||||
if e.stack != nil {
|
||||
_, _ = e.stack.WriteTo(w)
|
||||
}
|
||||
}
|
||||
|
||||
// with prev error
|
||||
if e.prev != nil {
|
||||
_, _ = io.WriteString(w, "\nPrevious: ")
|
||||
|
||||
if ex, ok := e.prev.(*ErrorX); ok {
|
||||
_, _ = ex.WriteTo(w)
|
||||
} else {
|
||||
_, _ = io.WriteString(w, e.prev.Error())
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Message error message of current
|
||||
func (e *ErrorX) Message() string {
|
||||
return e.msg
|
||||
}
|
||||
|
||||
// StackString returns error stack string of current.
|
||||
func (e *ErrorX) StackString() string {
|
||||
if e.stack != nil {
|
||||
return e.stack.String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// writeMsgTo write the error msg to a writer
|
||||
func (e *ErrorX) writeMsgTo(w io.Writer) {
|
||||
// current error
|
||||
if len(e.msg) > 0 {
|
||||
_, _ = w.Write([]byte(e.msg))
|
||||
}
|
||||
|
||||
// with prev error
|
||||
if e.prev != nil {
|
||||
_, _ = w.Write([]byte("; "))
|
||||
if ex, ok := e.prev.(*ErrorX); ok {
|
||||
ex.writeMsgTo(w)
|
||||
} else {
|
||||
_, _ = io.WriteString(w, e.prev.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CallerFunc returns the error caller func. if stack is nil, will return nil
|
||||
func (e *ErrorX) CallerFunc() *Func {
|
||||
if e.stack == nil {
|
||||
return nil
|
||||
}
|
||||
return FuncForPC(e.stack.CallerPC())
|
||||
}
|
||||
|
||||
// Location information for the caller func. more please see CallerFunc
|
||||
//
|
||||
// Returns eg:
|
||||
//
|
||||
// github.com/gookit/goutil/errorx_test.TestWithPrev(), errorx_test.go:34
|
||||
func (e *ErrorX) Location() string {
|
||||
if e.stack == nil {
|
||||
return "unknown"
|
||||
}
|
||||
return e.CallerFunc().Location()
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* new error with call stacks
|
||||
*************************************************************/
|
||||
|
||||
// New error message and with caller stacks
|
||||
func New(msg string) error {
|
||||
return &ErrorX{
|
||||
msg: msg,
|
||||
stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth),
|
||||
}
|
||||
}
|
||||
|
||||
// Newf error with format message, and with caller stacks.
|
||||
// alias of Errorf()
|
||||
func Newf(tpl string, vars ...any) error {
|
||||
return &ErrorX{
|
||||
msg: fmt.Sprintf(tpl, vars...),
|
||||
stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth),
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf error with format message, and with caller stacks
|
||||
func Errorf(tpl string, vars ...any) error {
|
||||
return &ErrorX{
|
||||
msg: fmt.Sprintf(tpl, vars...),
|
||||
stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth),
|
||||
}
|
||||
}
|
||||
|
||||
// With prev error and error message, and with caller stacks
|
||||
func With(err error, msg string) error {
|
||||
return &ErrorX{
|
||||
msg: msg,
|
||||
prev: err,
|
||||
stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth),
|
||||
}
|
||||
}
|
||||
|
||||
// Withf error and with format message, and with caller stacks
|
||||
func Withf(err error, tpl string, vars ...any) error {
|
||||
return &ErrorX{
|
||||
msg: fmt.Sprintf(tpl, vars...),
|
||||
prev: err,
|
||||
stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth),
|
||||
}
|
||||
}
|
||||
|
||||
// WithPrev error and message, and with caller stacks. alias of With()
|
||||
func WithPrev(err error, msg string) error {
|
||||
return &ErrorX{
|
||||
msg: msg,
|
||||
prev: err,
|
||||
stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth),
|
||||
}
|
||||
}
|
||||
|
||||
// WithPrevf error and with format message, and with caller stacks. alias of Withf()
|
||||
func WithPrevf(err error, tpl string, vars ...any) error {
|
||||
return &ErrorX{
|
||||
msg: fmt.Sprintf(tpl, vars...),
|
||||
prev: err,
|
||||
stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth),
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* wrap go error with call stacks
|
||||
*************************************************************/
|
||||
|
||||
// WithStack wrap a go error with a stacked trace. If err is nil, will return nil.
|
||||
func WithStack(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &ErrorX{
|
||||
msg: err.Error(),
|
||||
// prev: err,
|
||||
stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth),
|
||||
}
|
||||
}
|
||||
|
||||
// Traced warp a go error and with caller stacks. alias of WithStack()
|
||||
func Traced(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &ErrorX{
|
||||
msg: err.Error(),
|
||||
stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth),
|
||||
}
|
||||
}
|
||||
|
||||
// Stacked warp a go error and with caller stacks. alias of WithStack()
|
||||
func Stacked(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &ErrorX{
|
||||
msg: err.Error(),
|
||||
stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth),
|
||||
}
|
||||
}
|
||||
|
||||
// WithOptions new error with some option func
|
||||
func WithOptions(msg string, fns ...func(opt *ErrStackOpt)) error {
|
||||
opt := newErrOpt()
|
||||
for _, fn := range fns {
|
||||
fn(opt)
|
||||
}
|
||||
|
||||
return &ErrorX{
|
||||
msg: msg,
|
||||
stack: callersStack(opt.SkipDepth, opt.TraceDepth),
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* helper func for wrap error without stacks
|
||||
*************************************************************/
|
||||
|
||||
// Wrap error and with message, but not with stack
|
||||
func Wrap(err error, msg string) error {
|
||||
if err == nil {
|
||||
return errors.New(msg)
|
||||
}
|
||||
|
||||
return &ErrorX{
|
||||
msg: msg,
|
||||
prev: err,
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapf error with format message, but not with stack
|
||||
func Wrapf(err error, tpl string, vars ...any) error {
|
||||
if err == nil {
|
||||
return fmt.Errorf(tpl, vars...)
|
||||
}
|
||||
|
||||
return &ErrorX{
|
||||
msg: fmt.Sprintf(tpl, vars...),
|
||||
prev: err,
|
||||
}
|
||||
}
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
package errorx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// stack represents a stack of program counters.
|
||||
type stack []uintptr
|
||||
|
||||
// Format stack trace
|
||||
func (s *stack) Format(fs fmt.State, verb rune) {
|
||||
switch verb {
|
||||
// case 'v', 's':
|
||||
case 'v':
|
||||
_, _ = s.WriteTo(fs)
|
||||
}
|
||||
}
|
||||
|
||||
// StackLen for error
|
||||
func (s *stack) StackLen() int {
|
||||
return len(*s)
|
||||
}
|
||||
|
||||
// WriteTo for error
|
||||
func (s *stack) WriteTo(w io.Writer) (int64, error) {
|
||||
if len(*s) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
nn, _ := w.Write([]byte("\nSTACK:\n"))
|
||||
for _, pc := range *s {
|
||||
// For historical reasons if pc is interpreted as a uintptr
|
||||
// its value represents the program counter + 1.
|
||||
fc := runtime.FuncForPC(pc - 1)
|
||||
if fc == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// file eg: workspace/godev/gookit/goutil/errorx/errorx_test.go
|
||||
file, line := fc.FileLine(pc - 1)
|
||||
// f.Name() eg: github.com/gookit/goutil/errorx_test.TestWithPrev()
|
||||
location := fc.Name() + "()\n " + file + ":" + strconv.Itoa(line) + "\n"
|
||||
|
||||
n, _ := w.Write([]byte(location))
|
||||
nn += n
|
||||
}
|
||||
|
||||
return int64(nn), nil
|
||||
}
|
||||
|
||||
// String format to string
|
||||
func (s *stack) String() string {
|
||||
var buf bytes.Buffer
|
||||
_, _ = s.WriteTo(&buf)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// StackFrames stack frame list
|
||||
func (s *stack) StackFrames() *runtime.Frames {
|
||||
return runtime.CallersFrames(*s)
|
||||
}
|
||||
|
||||
// CallerPC the caller PC value in the stack. it is first frame.
|
||||
func (s *stack) CallerPC() uintptr {
|
||||
if len(*s) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// For historical reasons if pc is interpreted as a uintptr
|
||||
// its value represents the program counter + 1.
|
||||
return (*s)[0] - 1
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* For error caller func
|
||||
*************************************************************/
|
||||
|
||||
// Func struct
|
||||
type Func struct {
|
||||
*runtime.Func
|
||||
pc uintptr
|
||||
}
|
||||
|
||||
// FuncForPC create.
|
||||
func FuncForPC(pc uintptr) *Func {
|
||||
fc := runtime.FuncForPC(pc)
|
||||
if fc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &Func{
|
||||
pc: pc,
|
||||
Func: fc,
|
||||
}
|
||||
}
|
||||
|
||||
// FileLine returns the file name and line number of the source code
|
||||
func (f *Func) FileLine() (file string, line int) {
|
||||
return f.Func.FileLine(f.pc)
|
||||
}
|
||||
|
||||
// Location simple location info for the func
|
||||
//
|
||||
// Returns eg:
|
||||
//
|
||||
// "github.com/gookit/goutil/errorx_test.TestWithPrev(), errorx_test.go:34"
|
||||
func (f *Func) Location() string {
|
||||
file, line := f.FileLine()
|
||||
|
||||
return f.Name() + "(), " + filepath.Base(file) + ":" + strconv.Itoa(line)
|
||||
}
|
||||
|
||||
// String of the func
|
||||
//
|
||||
// Returns eg:
|
||||
//
|
||||
// github.com/gookit/goutil/errorx_test.TestWithPrev()
|
||||
// At /path/to/github.com/gookit/goutil/errorx_test.go:34
|
||||
func (f *Func) String() string {
|
||||
file, line := f.FileLine()
|
||||
|
||||
return f.Name() + "()\n At " + file + ":" + strconv.Itoa(line)
|
||||
}
|
||||
|
||||
// MarshalText handle
|
||||
func (f *Func) MarshalText() ([]byte, error) {
|
||||
return []byte(f.String()), nil
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* helper func for callers stacks
|
||||
*************************************************************/
|
||||
|
||||
// ErrStackOpt struct
|
||||
type ErrStackOpt struct {
|
||||
SkipDepth int
|
||||
TraceDepth int
|
||||
}
|
||||
|
||||
// default option
|
||||
var stdOpt = newErrOpt()
|
||||
|
||||
// ResetStdOpt config
|
||||
func ResetStdOpt() {
|
||||
stdOpt = newErrOpt()
|
||||
}
|
||||
|
||||
func newErrOpt() *ErrStackOpt {
|
||||
return &ErrStackOpt{
|
||||
SkipDepth: 3,
|
||||
TraceDepth: 8,
|
||||
}
|
||||
}
|
||||
|
||||
// Config the stdOpt setting
|
||||
func Config(fns ...func(opt *ErrStackOpt)) {
|
||||
for _, fn := range fns {
|
||||
fn(stdOpt)
|
||||
}
|
||||
}
|
||||
|
||||
// SkipDepth setting
|
||||
func SkipDepth(skipDepth int) func(opt *ErrStackOpt) {
|
||||
return func(opt *ErrStackOpt) {
|
||||
opt.SkipDepth = skipDepth
|
||||
}
|
||||
}
|
||||
|
||||
// TraceDepth setting
|
||||
func TraceDepth(traceDepth int) func(opt *ErrStackOpt) {
|
||||
return func(opt *ErrStackOpt) {
|
||||
opt.TraceDepth = traceDepth
|
||||
}
|
||||
}
|
||||
|
||||
func callersStack(skip, depth int) *stack {
|
||||
pcs := make([]uintptr, depth)
|
||||
num := runtime.Callers(skip, pcs[:])
|
||||
|
||||
var st stack = pcs[0:num]
|
||||
return &st
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
package errorx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// E new a raw go error. alias of errors.New()
|
||||
func E(msg string) error { return errors.New(msg) }
|
||||
|
||||
// Err new a raw go error. alias of errors.New()
|
||||
func Err(msg string) error { return errors.New(msg) }
|
||||
|
||||
// Raw new a raw go error. alias of errors.New()
|
||||
func Raw(msg string) error { return errors.New(msg) }
|
||||
|
||||
// Ef new a raw go error. alias of fmt.Errorf
|
||||
func Ef(tpl string, vars ...any) error { return fmt.Errorf(tpl, vars...) }
|
||||
|
||||
// Errf new a raw go error. alias of fmt.Errorf
|
||||
func Errf(tpl string, vars ...any) error { return fmt.Errorf(tpl, vars...) }
|
||||
|
||||
// Rf new a raw go error. alias of fmt.Errorf
|
||||
func Rf(tpl string, vs ...any) error { return fmt.Errorf(tpl, vs...) }
|
||||
|
||||
// Rawf new a raw go error. alias of fmt.Errorf
|
||||
func Rawf(tpl string, vs ...any) error { return fmt.Errorf(tpl, vs...) }
|
||||
|
||||
/*************************************************************
|
||||
* helper func for error
|
||||
*************************************************************/
|
||||
|
||||
// Cause returns the first cause error by call err.Cause().
|
||||
// Otherwise, will returns current error.
|
||||
func Cause(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err, ok := err.(Causer); ok {
|
||||
return err.Cause()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Unwrap returns previous error by call err.Unwrap().
|
||||
// Otherwise, will returns nil.
|
||||
func Unwrap(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err, ok := err.(Unwrapper); ok {
|
||||
return err.Unwrap()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Previous alias of Unwrap()
|
||||
func Previous(err error) error { return Unwrap(err) }
|
||||
|
||||
// IsErrorX check
|
||||
func IsErrorX(err error) (ok bool) {
|
||||
_, ok = err.(*ErrorX)
|
||||
return
|
||||
}
|
||||
|
||||
// ToErrorX convert check. like errors.As()
|
||||
func ToErrorX(err error) (ex *ErrorX, ok bool) {
|
||||
ex, ok = err.(*ErrorX)
|
||||
return
|
||||
}
|
||||
|
||||
// MustEX convert error to *ErrorX, panic if err check failed.
|
||||
func MustEX(err error) *ErrorX {
|
||||
ex, ok := err.(*ErrorX)
|
||||
if !ok {
|
||||
panic("errorx: error is not *ErrorX")
|
||||
}
|
||||
return ex
|
||||
}
|
||||
|
||||
// Has contains target error, or err is eq target.
|
||||
// alias of errors.Is()
|
||||
func Has(err, target error) bool {
|
||||
return errors.Is(err, target)
|
||||
}
|
||||
|
||||
// Is alias of errors.Is()
|
||||
func Is(err, target error) bool {
|
||||
return errors.Is(err, target)
|
||||
}
|
||||
|
||||
// To try convert err to target, returns is result.
|
||||
//
|
||||
// NOTICE: target must be ptr and not nil. alias of errors.As()
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// var ex *errorx.ErrorX
|
||||
// err := doSomething()
|
||||
// if errorx.To(err, &ex) {
|
||||
// fmt.Println(ex.GoString())
|
||||
// }
|
||||
func To(err error, target any) bool {
|
||||
return errors.As(err, target)
|
||||
}
|
||||
|
||||
// As same of the To(), alias of errors.As()
|
||||
//
|
||||
// NOTICE: target must be ptr and not nil
|
||||
func As(err error, target any) bool {
|
||||
return errors.As(err, target)
|
||||
}
|
||||
Reference in New Issue
Block a user