use std::borrow::Cow;
use thiserror::Error;
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum Error {
#[error("{0}")]
Transport(#[from] crate::transport::TransportError),
#[error("{0}")]
TokenSource(#[from] crate::token_source::TokenSourceError),
#[error("result code is: {result_code:?}, detail: {detail:?})")]
FailedMessage {
result_code: crate::message::ResultCode,
detail: String,
},
#[error("timeout {0}")]
Timeout(Cow<'static, str>),
#[error("connection closed")]
ConnectionClosed,
#[error("cancelled by close")]
CancelledByClose,
#[error("stream closed")]
StreamClosed,
#[error("unexpected: {0}")]
Unexpected(Cow<'static, str>),
#[error("invalid value `{0}`")]
InvalidValue(Cow<'static, str>),
#[error("cannot wait chunk in reordering, the upstream id is `{0}`")]
Reordering(uuid::Uuid),
}
impl Error {
pub(crate) fn can_retry(&self) -> bool {
matches!(
self,
Error::Transport(..) | Error::ConnectionClosed | Error::CancelledByClose
)
}
pub(crate) fn can_retry_resume(&self) -> bool {
self.can_retry() || matches!(self, Error::Timeout(..) | Error::Unexpected(..))
}
pub(crate) fn result_code(&self) -> Option<crate::message::ResultCode> {
match self {
Error::FailedMessage { result_code, .. } => Some(*result_code),
_ => None,
}
}
pub(crate) fn timeout<T: Into<Cow<'static, str>>>(msg: T) -> Self {
Self::Timeout(msg.into())
}
pub(crate) fn unexpected<T: Into<Cow<'static, str>>>(msg: T) -> Self {
Self::Unexpected(msg.into())
}
pub(crate) fn invalid_value<T: Into<Cow<'static, str>>>(msg: T) -> Self {
Self::InvalidValue(msg.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_retry_resume_includes_timeout_and_unexpected() {
assert!(Error::timeout("resume").can_retry_resume());
assert!(Error::unexpected("internal channel closed").can_retry_resume());
assert!(Error::ConnectionClosed.can_retry_resume());
assert!(Error::CancelledByClose.can_retry_resume());
}
#[test]
fn can_retry_resume_excludes_non_retryable() {
assert!(!Error::StreamClosed.can_retry_resume());
assert!(!Error::Reordering(uuid::Uuid::nil()).can_retry_resume());
}
#[test]
fn can_retry_unchanged_for_timeout() {
assert!(!Error::timeout("x").can_retry());
assert!(!Error::unexpected("x").can_retry());
}
}