144 lines
3.7 KiB
Go
144 lines
3.7 KiB
Go
package errorx
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/yuninks/langx"
|
|
)
|
|
|
|
// ErrorInfo 是对外暴露的错误信息接口,语义精简、不可变。
|
|
type ErrorInfo interface {
|
|
error
|
|
Code() int
|
|
Key() string
|
|
Format() map[string]string
|
|
}
|
|
|
|
// langError 是不可变的运行时错误实例,并发安全。
|
|
// 通过 With* 方法派生新实例,永远不会修改自身。
|
|
type langError struct {
|
|
ctx context.Context
|
|
key string
|
|
format map[string]string
|
|
}
|
|
|
|
// ---- 实现 error 接口 -----------------------------------------------
|
|
|
|
func (e *langError) Error() string {
|
|
return langx.GetFormat(langx.GetCtxLang(e.ctx), e.key, e.format)
|
|
}
|
|
|
|
// ---- 实现 ErrorInfo 接口 --------------------------------------------
|
|
|
|
func (e *langError) Code() int { return langx.GetCode(e.key) }
|
|
|
|
func (e *langError) Key() string { return e.key }
|
|
|
|
func (e *langError) Format() map[string]string {
|
|
if len(e.format) == 0 {
|
|
return nil
|
|
}
|
|
return e.copyFormat()
|
|
}
|
|
|
|
// ---- 不可变派生方法(返回新实例,不修改原值)------------------------
|
|
|
|
// WithCtx 派生一个携带新上下文的错误实例。
|
|
func (e *langError) WithCtx(ctx context.Context) *langError {
|
|
return &langError{ctx: ctx, key: e.key, format: e.copyFormat()}
|
|
}
|
|
|
|
// WithLang 派生一个指定语言的错误实例。
|
|
func (e *langError) WithLang(lang string) *langError {
|
|
return &langError{ctx: langx.SetCtxLang(e.ctx, lang), key: e.key, format: e.copyFormat()}
|
|
}
|
|
|
|
// WithKV 派生一个添加单个占位符键值对的错误实例。
|
|
func (e *langError) WithKV(k, v string) *langError {
|
|
newFmt := e.copyFormat()
|
|
if newFmt == nil {
|
|
newFmt = make(map[string]string)
|
|
}
|
|
newFmt[k] = v
|
|
return &langError{ctx: e.ctx, key: e.key, format: newFmt}
|
|
}
|
|
|
|
// WithMap 派生一个批量添加占位符键值对的错误实例(新 map 完全替换)。
|
|
func (e *langError) WithMap(m map[string]string) *langError {
|
|
newFmt := make(map[string]string, len(m))
|
|
for k, v := range m {
|
|
newFmt[k] = v
|
|
}
|
|
return &langError{ctx: e.ctx, key: e.key, format: newFmt}
|
|
}
|
|
|
|
// ---- 内部工具 --------------------------------------------------------
|
|
|
|
func (e *langError) copyFormat() map[string]string {
|
|
if len(e.format) == 0 {
|
|
return nil
|
|
}
|
|
dst := make(map[string]string, len(e.format))
|
|
for k, v := range e.format {
|
|
dst[k] = v
|
|
}
|
|
return dst
|
|
}
|
|
|
|
func copyMapString(src map[string]string) map[string]string {
|
|
if len(src) == 0 {
|
|
return nil
|
|
}
|
|
dst := make(map[string]string, len(src))
|
|
for k, v := range src {
|
|
dst[k] = v
|
|
}
|
|
return dst
|
|
}
|
|
|
|
// ---- 构造函数 --------------------------------------------------------
|
|
|
|
// NewError 创建一个携带 ctx 的运行时错误。
|
|
func NewError(ctx context.Context, key string) *langError {
|
|
return &langError{ctx: ctx, key: key}
|
|
}
|
|
|
|
// NewErrorf 创建一个携带占位符键值对的运行时错误。
|
|
func NewErrorf(ctx context.Context, key string, kv map[string]string) *langError {
|
|
return &langError{ctx: ctx, key: key, format: copyMapString(kv)}
|
|
}
|
|
|
|
// ---- 辅助函数,简化 error → ErrorInfo 提取 ---------------------------
|
|
|
|
// As 从 error 链中提取 *langError,失败返回 false。
|
|
func As(err error) (*langError, bool) {
|
|
var e *langError
|
|
ok := errors.As(err, &e)
|
|
return e, ok
|
|
}
|
|
|
|
// CodeFrom 从 error 中获取错误码;若不是 langError 则返回 -1。
|
|
func CodeFrom(err error) int {
|
|
if e, ok := As(err); ok {
|
|
return e.Code()
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// KeyFrom 从 error 中获取 Key;若不是 langError 则返回空串。
|
|
func KeyFrom(err error) string {
|
|
if e, ok := As(err); ok {
|
|
return e.Key()
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// FormatFrom 从 error 中获取占位符键值对(深拷贝);若不是 langError 则返回 nil。
|
|
func FormatFrom(err error) map[string]string {
|
|
if e, ok := As(err); ok {
|
|
return e.Format()
|
|
}
|
|
return nil
|
|
}
|