package fserr import ( "errors" "fmt" "strconv" "strings" ) // New 创建新的错误,支持格式化占位符 func New(format string, args ...any) error { msg := format if len(args) > 0 { msg = fmt.Sprintf(format, args...) } return &fundamental{ msg: msg, stack: callers(), } } // Wrap 包装已有错误,支持格式化占位符 func Wrap(err error, format string, args ...any) error { if err == nil { return nil } msg := format if len(args) > 0 { msg = fmt.Sprintf(format, args...) } return &withMessage{ cause: wrapStack(err), msg: msg, } } // WithCode 创建带有错误码的error,支持格式化占位符 // 使用option可以替换其中信息 func WithCode[T codeType](err error, businessCode T, options ...Option) error { code := getCode(serviceCode + int(businessCode)) ret := &withCode{ cause: wrapStack(err), Msg: code.Message, HttpCode: code.HttpCode, BusinessCode: code.BusinessCode, } for _, option := range options { option(ret) } if err == nil { return &withStack{ error: ret, stack: callers(), } } return ret } func WithStack(err error) error { return &withStack{ error: err, stack: callers(), } } // UnWrap 获取包装过的error // 若error1使用 Wrap 包装后产出错误error2 // 当使用 UmWrap 后,返回的是error1 func UnWrap(err error) error { type causer interface { Cause() error } for err != nil { cause, ok := err.(causer) if !ok { break } err = cause.Cause() } return err } // Is 判断err是否是target类型 // 相比==判断错误,该方式会进行不断地类似于 UmWrap 的操作, // 将 UmWrap 后的错误进行比较 func Is(err, target error) bool { return errors.Is(err, target) } // As 匹配最外层的与target类型相同的error,将其赋值给target func As(err error, target any) bool { return errors.As(err, target) } // ParseCode 将错误解析为错误码错误 // 若err不是错误码错误,则包裹传递错误,其他信息为默认错误码信息 // 若err为错误码错误,将其转换,将最外层错误信息作为最终错误信息返回 // 若想得到原始的错误码错误,可以使用As方法 func ParseCode(err error) *withCode { var target *withCode if !As(err, &target) { return &withCode{ cause: nil, Msg: err.Error(), HttpCode: defaultErrCode.HttpCode, BusinessCode: serviceCode * 10000, } } return &withCode{ cause: target.cause, Msg: outerMsg(err), HttpCode: target.HttpCode, BusinessCode: target.BusinessCode, } } // IsCode 判断某个错误是否为某个错误码 func IsCode[T codeType](err error, code T) bool { var target *withCode if !As(err, &target) { return false } return target.BusinessCode == serviceCode+int(code) } // SetAppCode 设置服务错误码 // 例如设置服务码为300000,用户不存在错误码为2001, // 那么最终业务错误码为302001 func SetAppCode[T codeType](code T) { strCode := strconv.Itoa(int(code)) if len(strCode) == 1 { strCode = "0" + strCode } c, err := strconv.ParseInt(strCode+strings.Repeat("0", 6-len(strCode)), 10, 64) if err != nil { panic(err) } serviceCode = int(c) } // outerMsg 获取最外层的错误信息 func outerMsg(err error) string { if err == nil { return "" } if e, ok := err.(*withCode); ok { return e.Msg } if e, ok := err.(*withMessage); ok { return e.msg } if e, ok := err.(*withStack); ok { return outerMsg(e.error) } return err.Error() } func wrapStack(err error) error { if _, ok := err.(*fundamental); ok { return err } if _, ok := err.(*withCode); ok { return err } if _, ok := err.(*withMessage); ok { return err } if _, ok := err.(*withStack); ok { return err } return &withStack{ error: err, stack: callers(), } }