Initial QSfera import

This commit is contained in:
Курнат Андрей
2026-06-07 10:20:04 +03:00
commit 2315f25754
16485 changed files with 4826827 additions and 0 deletions
+221
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}