package WrappedError import ( // Standard "fmt" "path" "runtime" ) type Error struct { Wrapped error ErrStr string // wrapped error isn't necessarily json encodable Code string `json:",omitempty"` File string Line int Data any `json:",omitempty"` } type CodableError interface { error WithCode(string) CodableError WithData(any) CodableError } type LogCallback func(Error) var ( logCallback LogCallback baseDirLength int ) // Init only works if called from the main package and sets the length of code base path // to later be removed in file paths to receive relative paths. func Init() { _, file, _, _ := runtime.Caller(1) dirBase := path.Dir(file) baseDirLength = len(dirBase) } // SetLogCallback gives a possibility to automatically run code to handle any errors. func SetLogCallback(cbk LogCallback) { logCallback = cbk } func callback(wrapped Error) { if logCallback != nil { logCallback(wrapped) } } // Error implements the error inteface and adds filename and line to the error. func (wrapped Error) Error() string { var code string if wrapped.Code != "" { code = wrapped.Code + " " } return fmt.Sprintf( "%s[%s:%d] %s", code, wrapped.File, wrapped.Line, wrapped.Wrapped.Error(), ) } func create(err error, data interface{}) *Error { if err == nil { return nil } _, file, line, _ := runtime.Caller(2) file = file[baseDirLength+1:] wrapped := Error{ Wrapped: err, ErrStr: err.Error(), File: file, Line: line, Data: data, } callback(wrapped) return &wrapped } // Wrap wraps an existing error with file and line. func Wrap(err error) *Error { return create(err, nil) } func WrapData(err error, data interface{}) *Error { return create(err, data) } // New creates a new wrapped error with file and line. func New(msg string, params ...any) *Error { err := fmt.Errorf(msg, params...) wrapped := create(err, nil) return wrapped } // NewData creates a new WrappedError with associated data. func NewData(msg string, data interface{}, params ...any) *Error { err := fmt.Errorf(msg, params...) wrapped := create(err, data) return wrapped } func (e *Error) WithCode(code string) CodableError { e.Code = code return e } func (e *Error) WithData(data any) CodableError { e.Data = data return e }