package errs import ( "encoding/binary" "fmt" "io" "runtime/debug" ) type Error struct { msg string code int64 collection string index string stackTrace string err error // Wrapped error } func NewErr(code int64, msg string) *Error { return &Error{ msg: msg, code: code, } } func (e *Error) Error() string { if e.collection != "" || e.index != "" { return fmt.Sprintf(`[%d] (%s/%s) %s`, e.code, e.collection, e.index, e.msg) } else { return fmt.Sprintf("[%d] %s", e.code, e.msg) } } func (e *Error) Is(rhs error) bool { e2, ok := rhs.(*Error) if !ok { return false } return e.code == e2.code } func (e *Error) WithErr(err error) *Error { if e2, ok := err.(*Error); ok && e2.code == e.code { return e2 } e2 := e.WithMsg(err.Error()) e2.err = err return e2 } func (e *Error) Unwrap() error { if e.err != nil { return e.err } return e } func (e *Error) WithMsg(msg string, args ...any) *Error { err := *e err.msg += ": " + fmt.Sprintf(msg, args...) if len(err.stackTrace) == 0 { err.stackTrace = string(debug.Stack()) } return &err } func (e *Error) WithCollection(s string) *Error { err := *e err.collection = s return &err } func (e *Error) WithIndex(s string) *Error { err := *e err.index = s return &err } func (e *Error) msgTruncacted() string { if len(e.msg) > 255 { return e.msg[:255] } return e.msg } func (e *Error) Write(w io.Writer) error { msg := e.msgTruncacted() if err := binary.Write(w, binary.LittleEndian, e.code); err != nil { return IO.WithErr(err) } if _, err := w.Write([]byte{byte(len(msg))}); err != nil { return err } _, err := w.Write([]byte(msg)) return err } func (e *Error) Read(r io.Reader) error { var ( size uint8 ) if err := binary.Read(r, binary.LittleEndian, &e.code); err != nil { return IO.WithErr(err) } if err := binary.Read(r, binary.LittleEndian, &size); err != nil { return IO.WithErr(err) } msgBuf := make([]byte, size) if _, err := io.ReadFull(r, msgBuf); err != nil { return IO.WithErr(err) } e.msg = string(msgBuf) return nil }