package WrappedError import ( // Standard "fmt" "path" "regexp" "runtime" ) type Error struct { Wrapped error ErrStr string // wrapped error isn't necessarily json encodable Code string File string Line int Data any } type CodableError interface { error WithCode(string) CodableError WithData(any) CodableError Log() 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, } return &wrapped } // Wrap wraps an existing error with file and line. func Wrap(err error) *Error { return create(err, nil) } // 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 } // WithCode associates a string code with the error. func (e *Error) WithCode(code string) CodableError { e.Code = code return e } // WithData associates any data with the error to make troubleshooting easier. func (e *Error) WithData(data any) CodableError { e.Data = data return e } // Log calls the log callback. func (e *Error) Log() CodableError { callback(*e) return e } // NoTrace returns the Error() string without the trace. func (e *Error) NoTrace() string { rxp := regexp.MustCompile(`^(\[[^\]]+\.go:\d+\]\s*)*`) return string( rxp.ReplaceAll( []byte(e.Error()), []byte(""), ), ) }