package merrors import ( "errors" "fmt" "strings" ) type definedError string func (e definedError) Error() string { return string(e) } type wrappedError struct { base error msg string } // opaqueWrappedError preserves error classification and causal inspection // without including the cause text in Error. Use it at boundaries where a // storage or provider error can contain secrets or sensitive identifiers. type opaqueWrappedError struct { base error cause error msg string } func (e wrappedError) Error() string { baseText := "" if e.base != nil { baseText = strings.TrimSpace(e.base.Error()) } msg := strings.TrimSpace(e.msg) switch { case baseText == "": return msg case msg == "": return baseText default: return baseText + ": " + msg } } func (e wrappedError) Unwrap() error { return e.base } func (e opaqueWrappedError) Error() string { return Wrap(e.base, e.msg).Error() } func (e opaqueWrappedError) Unwrap() []error { causes := make([]error, 0, 2) if e.base != nil { causes = append(causes, e.base) } if e.cause != nil { causes = append(causes, e.cause) } return causes } func Define(msg string) error { return definedError(msg) } func Message(msg string) error { return definedError(msg) } func Messagef(format string, args ...any) error { return Message(fmt.Sprintf(format, args...)) } func Wrap(err error, msg string) error { return wrappedError{ base: err, msg: msg, } } func Wrapf(err error, format string, args ...any) error { return Wrap(err, fmt.Sprintf(format, args...)) } // OpaqueWrap adds safe context while retaining the cause for errors.Is and // errors.As. Unlike Wrap, it deliberately excludes the cause text from Error. func OpaqueWrap(base, cause error, msg string) error { return opaqueWrappedError{base: base, cause: cause, msg: msg} } var ErrNotImplemented = Define("notImplemented") func NotImplemented(msg string) error { return Wrap(ErrNotImplemented, msg) } var ErrNoData = Define("noData") func NoData(msg string) error { return Wrap(ErrNoData, msg) } func NoDataf(format string, args ...any) error { return NoData(fmt.Sprintf(format, args...)) } var ErrInternal = Define("internal") func Internal(msg string) error { return Wrap(ErrInternal, msg) } func Internalf(format string, args ...any) error { return Internal(fmt.Sprintf(format, args...)) } var ErrInvalidArg = Define("invalidArgError") func InvalidArgument(msg string, argumentNames ...string) error { return Wrap(ErrInvalidArg, invalidArgumentMessage(msg, argumentNames...)) } func InvalidArgumentf(format string, args ...any) error { return InvalidArgument(fmt.Sprintf(format, args...)) } func InvalidArgumentWithNamesf(argumentNames []string, format string, args ...any) error { return InvalidArgument(fmt.Sprintf(format, args...), argumentNames...) } var ErrDataConflict = Define("dataConflict") func DataConflict(msg string) error { return Wrap(ErrDataConflict, msg) } func DataConflictf(format string, args ...any) error { return DataConflict(fmt.Sprintf(format, args...)) } func DataConflictWrap(err error, msg string) error { return wrapError(ErrDataConflict, msg, err) } func DataConflictWrapf(err error, format string, args ...any) error { return DataConflictWrap(err, fmt.Sprintf(format, args...)) } var ErrFailedPrecondition = Define("failedPrecondition") func FailedPrecondition(msg string) error { return Wrap(ErrFailedPrecondition, msg) } func FailedPreconditionf(format string, args ...any) error { return FailedPrecondition(fmt.Sprintf(format, args...)) } func FailedPreconditionWrap(err error, msg string) error { return wrapError(ErrFailedPrecondition, msg, err) } func FailedPreconditionWrapf(err error, format string, args ...any) error { return FailedPreconditionWrap(err, fmt.Sprintf(format, args...)) } var ErrServiceUnavailable = Define("serviceUnavailable") func ServiceUnavailable(msg string) error { return Wrap(ErrServiceUnavailable, msg) } func ServiceUnavailablef(format string, args ...any) error { return ServiceUnavailable(fmt.Sprintf(format, args...)) } func ServiceUnavailableWrap(err error, msg string) error { return wrapError(ErrServiceUnavailable, msg, err) } func ServiceUnavailableWrapf(err error, format string, args ...any) error { return ServiceUnavailableWrap(err, fmt.Sprintf(format, args...)) } type objectRef interface { IsZero() bool Hex() string } var ErrAccessDenied = Define("accessDenied") func PermissionDenied(msg string) error { return Wrap(ErrAccessDenied, msg) } func AccessDenied(object, action string, ref objectRef) error { if ref != nil && !ref.IsZero() { return Wrap(ErrAccessDenied, fmt.Sprintf("cannot %s object of type '%s' with ID '%s'", action, object, ref.Hex())) } return Wrap(ErrAccessDenied, fmt.Sprintf("cannot %s object of type '%s'", action, object)) } var ErrInvalidDataType = Define("invalidDataType") func InvalidDataType(msg string) error { return Wrap(ErrInvalidDataType, msg) } func InvalidDataTypef(format string, args ...any) error { return InvalidDataType(fmt.Sprintf(format, args...)) } var ErrUnauthorized = Define("unathorized") func Unauthorized(msg string) error { return Wrap(ErrUnauthorized, msg) } func Unauthorizedf(format string, args ...any) error { return Unauthorized(fmt.Sprintf(format, args...)) } var ErrNoMessagingTopic = Define("messagingTopicError") func NoMessagingTopic(topic string) error { return Wrapf(ErrNoMessagingTopic, "messaging topic '%s' not found", topic) } func InvalidArgumentWrap(err error, msg string, argumentNames ...string) error { return wrapError(ErrInvalidArg, invalidArgumentMessage(msg, argumentNames...), err) } func InvalidArgumentWrapf(err error, format string, args ...any) error { return InvalidArgumentWrap(err, fmt.Sprintf(format, args...)) } func InternalWrap(err error, msg string) error { return wrapError(ErrInternal, msg, err) } func InternalWrapf(err error, format string, args ...any) error { return InternalWrap(err, fmt.Sprintf(format, args...)) } func InternalWrapAll(msg string, errs ...error) error { return wrapErrors(ErrInternal, msg, errs...) } func wrapError(base error, msg string, err error) error { return wrapErrors(base, msg, err) } func wrapErrors(base error, msg string, errs ...error) error { baseErr := Wrap(base, msg) causes := make([]error, 0, len(errs)) for _, err := range errs { if err != nil { causes = append(causes, err) } } if len(causes) == 0 { return baseErr } return errors.Join(baseErr, errors.Join(causes...)) }