1use std::borrow::Cow;
2
3use thiserror::Error;
4
5#[non_exhaustive]
7#[derive(Error, Debug)]
8pub enum Error {
9 #[error("{0}")]
14 Transport(#[from] crate::transport::TransportError),
15
16 #[error("{0}")]
21 TokenSource(#[from] crate::token_source::TokenSourceError),
22
23 #[error("result code is: {result_code:?}, detail: {detail:?})")]
28 FailedMessage {
29 result_code: crate::message::ResultCode,
31 detail: String,
33 },
34
35 #[error("timeout {0}")]
40 Timeout(Cow<'static, str>),
41
42 #[error("connection closed")]
46 ConnectionClosed,
47
48 #[error("cancelled by close")]
52 CancelledByClose,
53
54 #[error("stream closed")]
59 StreamClosed,
60
61 #[error("unexpected: {0}")]
66 Unexpected(Cow<'static, str>),
67
68 #[error("invalid value `{0}`")]
72 InvalidValue(Cow<'static, str>),
73
74 #[error("cannot wait chunk in reordering, the upstream id is `{0}`")]
78 Reordering(uuid::Uuid),
79}
80
81impl Error {
82 pub(crate) fn can_retry(&self) -> bool {
83 matches!(
84 self,
85 Error::Transport(..) | Error::ConnectionClosed | Error::CancelledByClose
86 )
87 }
88
89 pub(crate) fn can_retry_resume(&self) -> bool {
93 self.can_retry() || matches!(self, Error::Timeout(..) | Error::Unexpected(..))
94 }
95
96 pub(crate) fn result_code(&self) -> Option<crate::message::ResultCode> {
97 match self {
98 Error::FailedMessage { result_code, .. } => Some(*result_code),
99 _ => None,
100 }
101 }
102
103 pub(crate) fn timeout<T: Into<Cow<'static, str>>>(msg: T) -> Self {
104 Self::Timeout(msg.into())
105 }
106
107 pub(crate) fn unexpected<T: Into<Cow<'static, str>>>(msg: T) -> Self {
108 Self::Unexpected(msg.into())
109 }
110
111 pub(crate) fn invalid_value<T: Into<Cow<'static, str>>>(msg: T) -> Self {
112 Self::InvalidValue(msg.into())
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn can_retry_resume_includes_timeout_and_unexpected() {
122 assert!(Error::timeout("resume").can_retry_resume());
123 assert!(Error::unexpected("internal channel closed").can_retry_resume());
124 assert!(Error::ConnectionClosed.can_retry_resume());
125 assert!(Error::CancelledByClose.can_retry_resume());
126 }
127
128 #[test]
129 fn can_retry_resume_excludes_non_retryable() {
130 assert!(!Error::StreamClosed.can_retry_resume());
131 assert!(!Error::Reordering(uuid::Uuid::nil()).can_retry_resume());
132 }
133
134 #[test]
135 fn can_retry_unchanged_for_timeout() {
136 assert!(!Error::timeout("x").can_retry());
139 assert!(!Error::unexpected("x").can_retry());
140 }
141}