292 lines
6.0 KiB
Go
292 lines
6.0 KiB
Go
package errorx
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"io"
|
||
|
|
"strings"
|
||
|
|
)
|
||
|
|
|
||
|
|
type ServiceError interface {
|
||
|
|
Code() code
|
||
|
|
Error() string
|
||
|
|
}
|
||
|
|
|
||
|
|
type ServiceFieldError interface {
|
||
|
|
ServiceError
|
||
|
|
Field() string
|
||
|
|
}
|
||
|
|
|
||
|
|
type ServiceFieldErrors interface {
|
||
|
|
Errors() []error
|
||
|
|
}
|
||
|
|
|
||
|
|
// New returns an error with the supplied message.
|
||
|
|
// New also records the stack trace at the point it was called.
|
||
|
|
func New(code code, message ...string) error {
|
||
|
|
msg := strings.Join(message, " ")
|
||
|
|
return &fundamental{
|
||
|
|
code: code,
|
||
|
|
msg: msg,
|
||
|
|
stack: callers(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewDirectError(message ...string) error {
|
||
|
|
return New(DirectErrCode, message...)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Errorf formats according to a format specifier and returns the string
|
||
|
|
// as a value that satisfies error.
|
||
|
|
// Errorf also records the stack trace at the point it was called.
|
||
|
|
func Errorf(code code, format string, args ...interface{}) error {
|
||
|
|
return &fundamental{
|
||
|
|
code: code,
|
||
|
|
msg: fmt.Sprintf(format, args...),
|
||
|
|
stack: callers(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// fundamental is an error that has a message and a stack, but no caller.
|
||
|
|
type fundamental struct {
|
||
|
|
code code
|
||
|
|
msg string
|
||
|
|
*stack
|
||
|
|
}
|
||
|
|
|
||
|
|
func (f *fundamental) Code() code { return f.code }
|
||
|
|
|
||
|
|
func (f *fundamental) Error() string { return f.msg }
|
||
|
|
|
||
|
|
func (f *fundamental) Format(s fmt.State, verb rune) {
|
||
|
|
switch verb {
|
||
|
|
case 'v':
|
||
|
|
if s.Flag('+') {
|
||
|
|
_, _ = io.WriteString(s, fmt.Sprintf("%d", f.code))
|
||
|
|
_, _ = io.WriteString(s, f.msg)
|
||
|
|
f.stack.Format(s, verb)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
fallthrough
|
||
|
|
case 's':
|
||
|
|
_, _ = io.WriteString(s, f.msg)
|
||
|
|
case 'q':
|
||
|
|
_, _ = fmt.Fprintf(s, "%q", f.msg)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewFieldError(field string, message string) error {
|
||
|
|
return &fieldError{
|
||
|
|
fundamental: fundamental{
|
||
|
|
code: RequestParamInvalidWithField,
|
||
|
|
msg: message,
|
||
|
|
},
|
||
|
|
field: field,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewFieldErrorf(field string, format string, args ...interface{}) error {
|
||
|
|
return &fieldError{
|
||
|
|
fundamental: fundamental{
|
||
|
|
code: RequestParamInvalidWithField,
|
||
|
|
msg: fmt.Sprintf(format, args...),
|
||
|
|
},
|
||
|
|
field: field,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
type fieldError struct {
|
||
|
|
fundamental
|
||
|
|
field string
|
||
|
|
}
|
||
|
|
|
||
|
|
func (f *fieldError) Code() code { return f.code }
|
||
|
|
|
||
|
|
func (f *fieldError) Error() string { return f.msg }
|
||
|
|
|
||
|
|
func (f *fieldError) Field() string { return f.field }
|
||
|
|
|
||
|
|
func NewFieldErrors(fields map[string]string) error {
|
||
|
|
err := &fieldErrors{
|
||
|
|
fundamental: fundamental{
|
||
|
|
code: RequestParamInvalidWithField,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
for field, msg := range fields {
|
||
|
|
err.errors = append(err.errors, NewFieldError(field, msg))
|
||
|
|
}
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
type fieldErrors struct {
|
||
|
|
fundamental
|
||
|
|
errors []error
|
||
|
|
}
|
||
|
|
|
||
|
|
func (f *fieldErrors) Errors() []error { return f.errors }
|
||
|
|
|
||
|
|
// WithStack annotates err with a stack trace at the point WithStack was called.
|
||
|
|
// If err is nil, WithStack returns nil.
|
||
|
|
func WithStack(err error, code code) error {
|
||
|
|
if err == nil {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
return &withStack{
|
||
|
|
err,
|
||
|
|
code,
|
||
|
|
callers(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
type withStack struct {
|
||
|
|
error
|
||
|
|
code
|
||
|
|
*stack
|
||
|
|
}
|
||
|
|
|
||
|
|
func (w *withStack) Code() code { return w.code }
|
||
|
|
|
||
|
|
func (w *withStack) Cause() error { return w.error }
|
||
|
|
|
||
|
|
// Unwrap provides compatibility for Go 1.13 error chains.
|
||
|
|
func (w *withStack) Unwrap() error { return w.error }
|
||
|
|
|
||
|
|
func (w *withStack) Format(s fmt.State, verb rune) {
|
||
|
|
switch verb {
|
||
|
|
case 'v':
|
||
|
|
if s.Flag('+') {
|
||
|
|
_, _ = fmt.Fprintf(s, "%+v", w.Cause())
|
||
|
|
_, _ = io.WriteString(s, fmt.Sprintf(" code: %d", w.code))
|
||
|
|
w.stack.Format(s, verb)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
fallthrough
|
||
|
|
case 's':
|
||
|
|
_, _ = io.WriteString(s, w.Error())
|
||
|
|
case 'q':
|
||
|
|
_, _ = fmt.Fprintf(s, "%q", w.Error())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Wrap returns an error annotating err with a stack trace
|
||
|
|
// at the point Wrap is called, and the supplied message.
|
||
|
|
// If err is nil, Wrap returns nil.
|
||
|
|
func Wrap(err error, code code, message string) error {
|
||
|
|
if err == nil {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
err = &withMessage{
|
||
|
|
code: code,
|
||
|
|
cause: err,
|
||
|
|
msg: message,
|
||
|
|
}
|
||
|
|
return &withStack{
|
||
|
|
err,
|
||
|
|
code,
|
||
|
|
callers(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Wrapf returns an error annotating err with a stack trace
|
||
|
|
// at the point Wrapf is called, and the format specifier.
|
||
|
|
// If err is nil, Wrapf returns nil.
|
||
|
|
func Wrapf(err error, code code, format string, args ...interface{}) error {
|
||
|
|
if err == nil {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
err = &withMessage{
|
||
|
|
code: code,
|
||
|
|
cause: err,
|
||
|
|
msg: fmt.Sprintf(format, args...),
|
||
|
|
}
|
||
|
|
return &withStack{
|
||
|
|
err,
|
||
|
|
code,
|
||
|
|
callers(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// WithMessage annotates err with a new message.
|
||
|
|
// If err is nil, WithMessage returns nil.
|
||
|
|
func WithMessage(err error, code uint32, message string) error {
|
||
|
|
if err == nil {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
return &withMessage{
|
||
|
|
code: code,
|
||
|
|
cause: err,
|
||
|
|
msg: message,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// WithMessagef annotates err with the format specifier.
|
||
|
|
// If err is nil, WithMessagef returns nil.
|
||
|
|
func WithMessagef(err error, code uint32, format string, args ...interface{}) error {
|
||
|
|
if err == nil {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
return &withMessage{
|
||
|
|
code: code,
|
||
|
|
cause: err,
|
||
|
|
msg: fmt.Sprintf(format, args...),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
type withMessage struct {
|
||
|
|
code code
|
||
|
|
cause error
|
||
|
|
msg string
|
||
|
|
}
|
||
|
|
|
||
|
|
func (w *withMessage) Code() code {
|
||
|
|
return w.code
|
||
|
|
}
|
||
|
|
|
||
|
|
func (w *withMessage) Error() string {
|
||
|
|
return fmt.Sprintf("%d", w.code) + ": " + w.msg + ": " + w.cause.Error()
|
||
|
|
}
|
||
|
|
func (w *withMessage) Cause() error { return w.cause }
|
||
|
|
|
||
|
|
// Unwrap provides compatibility for Go 1.13 error chains.
|
||
|
|
func (w *withMessage) Unwrap() error { return w.cause }
|
||
|
|
|
||
|
|
func (w *withMessage) Format(s fmt.State, verb rune) {
|
||
|
|
switch verb {
|
||
|
|
case 'v':
|
||
|
|
if s.Flag('+') {
|
||
|
|
_, _ = fmt.Fprintf(s, "%+v\n", w.Cause())
|
||
|
|
_, _ = io.WriteString(s, fmt.Sprintf("code: %d ", w.code))
|
||
|
|
_, _ = io.WriteString(s, fmt.Sprintf(", msg: %s\n", w.msg))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
fallthrough
|
||
|
|
case 's', 'q':
|
||
|
|
_, _ = io.WriteString(s, w.Error())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Cause returns the underlying cause of the error, if possible.
|
||
|
|
// An error value has a cause if it implements the following
|
||
|
|
// interface:
|
||
|
|
//
|
||
|
|
// type causer interface {
|
||
|
|
// Cause() error
|
||
|
|
// }
|
||
|
|
//
|
||
|
|
// If the error does not implement Cause, the original error will
|
||
|
|
// be returned. If the error is nil, nil will be returned without further
|
||
|
|
// investigation.
|
||
|
|
func Cause(err error) error {
|
||
|
|
type causer interface {
|
||
|
|
Cause() error
|
||
|
|
}
|
||
|
|
|
||
|
|
for err != nil {
|
||
|
|
cause, ok := err.(causer)
|
||
|
|
if !ok {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
err = cause.Cause()
|
||
|
|
}
|
||
|
|
return err
|
||
|
|
}
|