1use std::sync::Arc;
2
3#[derive(Debug, Clone, thiserror::Error)]
9#[non_exhaustive]
10pub enum Error {
11 #[error(transparent)]
13 Io(Arc<std::io::Error>),
14
15 #[error(transparent)]
17 MoqNet(#[from] moq_net::Error),
18
19 #[error("invalid log directive")]
21 Directive(#[source] Arc<tracing_subscriber::filter::ParseError>),
22
23 #[error("failed to set global tracing subscriber")]
25 SetSubscriber(#[source] Arc<tracing_subscriber::util::TryInitError>),
26
27 #[error("failed to initialize Android logcat layer")]
29 Logcat(#[source] Arc<std::io::Error>),
30
31 #[error("{0}")]
33 NoBackend(&'static str),
34
35 #[error("qlog capture requires the 'qlog' feature")]
37 QlogUnsupported,
38
39 #[error("failed to connect to server")]
41 ConnectFailed,
42
43 #[error("connect timed out after {0:?}")]
51 ConnectTimeout(std::time::Duration),
52
53 #[error(transparent)]
55 Connect(#[from] crate::ConnectError),
56
57 #[cfg(feature = "websocket")]
59 #[error("failed to connect to server: QUIC failed: {quic}; WebSocket failed: {websocket}")]
60 TransportRace {
61 quic: Arc<Error>,
63 websocket: Arc<Error>,
65 },
66
67 #[cfg(feature = "iroh")]
69 #[error("Iroh support is not enabled")]
70 IrohDisabled,
71
72 #[error("tls.root (mTLS) is not supported by the selected QUIC backend")]
74 MtlsUnsupported,
75
76 #[error("invalid status code")]
78 InvalidStatusCode,
79
80 #[error("{0}")]
82 Reconnect(String),
83
84 #[error(transparent)]
86 Tls(Arc<crate::tls::Error>),
87
88 #[cfg(feature = "quinn")]
90 #[error(transparent)]
91 Quinn(Arc<crate::quinn::Error>),
92
93 #[cfg(feature = "noq")]
95 #[error(transparent)]
96 Noq(Arc<crate::noq::Error>),
97
98 #[cfg(feature = "quiche")]
100 #[error(transparent)]
101 Quiche(Arc<crate::quiche::Error>),
102
103 #[cfg(feature = "iroh")]
105 #[error(transparent)]
106 Iroh(Arc<crate::iroh::Error>),
107
108 #[cfg(feature = "websocket")]
110 #[error(transparent)]
111 WebSocket(Arc<crate::websocket::Error>),
112
113 #[cfg(feature = "tcp")]
115 #[error(transparent)]
116 Tcp(Arc<crate::tcp::Error>),
117
118 #[cfg(all(feature = "uds", unix))]
120 #[error(transparent)]
121 Unix(Arc<crate::unix::Error>),
122}
123
124impl Error {
125 pub fn connect_error(&self) -> Option<crate::ConnectError> {
127 match self {
128 Self::Connect(err) => Some(*err),
129 Self::MoqNet(moq_net::Error::Unauthorized) => Some(crate::ConnectError::Unauthorized),
130 #[cfg(feature = "quinn")]
131 Self::Quinn(err) => err.connect_error(),
132 #[cfg(feature = "noq")]
133 Self::Noq(err) => err.connect_error(),
134 #[cfg(feature = "quiche")]
135 Self::Quiche(err) => err.connect_error(),
136 #[cfg(feature = "websocket")]
137 Self::TransportRace { quic, websocket } => quic.connect_error().or_else(|| websocket.connect_error()),
138 #[cfg(feature = "websocket")]
139 Self::WebSocket(err) => err.connect_error(),
140 _ => None,
141 }
142 }
143
144 pub fn is_auth(&self) -> bool {
146 self.connect_error().is_some_and(|err| err.is_auth())
147 }
148}
149
150impl From<std::io::Error> for Error {
153 fn from(err: std::io::Error) -> Self {
154 Self::Io(Arc::new(err))
155 }
156}
157
158impl From<tracing_subscriber::filter::ParseError> for Error {
159 fn from(err: tracing_subscriber::filter::ParseError) -> Self {
160 Self::Directive(Arc::new(err))
161 }
162}
163
164impl From<crate::tls::Error> for Error {
165 fn from(err: crate::tls::Error) -> Self {
166 Self::Tls(Arc::new(err))
167 }
168}
169
170#[cfg(feature = "quinn")]
171impl From<crate::quinn::Error> for Error {
172 fn from(err: crate::quinn::Error) -> Self {
173 if let Some(err) = err.connect_error() {
174 return Self::Connect(err);
175 }
176
177 Self::Quinn(Arc::new(err))
178 }
179}
180
181#[cfg(feature = "noq")]
182impl From<crate::noq::Error> for Error {
183 fn from(err: crate::noq::Error) -> Self {
184 if let Some(err) = err.connect_error() {
185 return Self::Connect(err);
186 }
187
188 Self::Noq(Arc::new(err))
189 }
190}
191
192#[cfg(feature = "quiche")]
193impl From<crate::quiche::Error> for Error {
194 fn from(err: crate::quiche::Error) -> Self {
195 if let Some(err) = err.connect_error() {
196 return Self::Connect(err);
197 }
198
199 Self::Quiche(Arc::new(err))
200 }
201}
202
203#[cfg(feature = "iroh")]
204impl From<crate::iroh::Error> for Error {
205 fn from(err: crate::iroh::Error) -> Self {
206 Self::Iroh(Arc::new(err))
207 }
208}
209
210#[cfg(feature = "websocket")]
211impl From<crate::websocket::Error> for Error {
212 fn from(err: crate::websocket::Error) -> Self {
213 if let Some(err) = err.connect_error() {
214 return Self::Connect(err);
215 }
216
217 Self::WebSocket(Arc::new(err))
218 }
219}
220
221#[cfg(feature = "tcp")]
222impl From<crate::tcp::Error> for Error {
223 fn from(err: crate::tcp::Error) -> Self {
224 Self::Tcp(Arc::new(err))
225 }
226}
227
228#[cfg(all(feature = "uds", unix))]
229impl From<crate::unix::Error> for Error {
230 fn from(err: crate::unix::Error) -> Self {
231 Self::Unix(Arc::new(err))
232 }
233}
234
235pub type Result<T> = std::result::Result<T, Error>;
237
238#[cfg(all(test, feature = "websocket"))]
239mod tests {
240 use super::*;
241
242 #[test]
243 fn transport_race_propagates_nested_connect_errors() {
244 let quic = Error::TransportRace {
245 quic: Arc::new(crate::ConnectError::Unauthorized.into()),
246 websocket: Arc::new(crate::ConnectError::Forbidden.into()),
247 };
248 assert_eq!(quic.connect_error(), Some(crate::ConnectError::Unauthorized));
249
250 let websocket = Error::TransportRace {
251 quic: Arc::new(Error::ConnectFailed),
252 websocket: Arc::new(crate::ConnectError::Forbidden.into()),
253 };
254 assert_eq!(websocket.connect_error(), Some(crate::ConnectError::Forbidden));
255 }
256}