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(transparent)]
45 Connect(#[from] crate::ConnectError),
46
47 #[cfg(feature = "websocket")]
49 #[error("failed to connect to server: QUIC failed: {quic}; WebSocket failed: {websocket}")]
50 TransportRace {
51 quic: Arc<Error>,
53 websocket: Arc<Error>,
55 },
56
57 #[cfg(feature = "iroh")]
59 #[error("Iroh support is not enabled")]
60 IrohDisabled,
61
62 #[error("tls.root (mTLS) is not supported by the selected QUIC backend")]
64 MtlsUnsupported,
65
66 #[error("invalid status code")]
68 InvalidStatusCode,
69
70 #[error("{0}")]
72 Reconnect(String),
73
74 #[error(transparent)]
76 Tls(Arc<crate::tls::Error>),
77
78 #[cfg(feature = "quinn")]
80 #[error(transparent)]
81 Quinn(Arc<crate::quinn::Error>),
82
83 #[cfg(feature = "noq")]
85 #[error(transparent)]
86 Noq(Arc<crate::noq::Error>),
87
88 #[cfg(feature = "quiche")]
90 #[error(transparent)]
91 Quiche(Arc<crate::quiche::Error>),
92
93 #[cfg(feature = "iroh")]
95 #[error(transparent)]
96 Iroh(Arc<crate::iroh::Error>),
97
98 #[cfg(feature = "websocket")]
100 #[error(transparent)]
101 WebSocket(Arc<crate::websocket::Error>),
102
103 #[cfg(feature = "tcp")]
105 #[error(transparent)]
106 Tcp(Arc<crate::tcp::Error>),
107
108 #[cfg(all(feature = "uds", unix))]
110 #[error(transparent)]
111 Unix(Arc<crate::unix::Error>),
112}
113
114impl Error {
115 pub fn connect_error(&self) -> Option<crate::ConnectError> {
117 match self {
118 Self::Connect(err) => Some(*err),
119 Self::MoqNet(moq_net::Error::Unauthorized) => Some(crate::ConnectError::Unauthorized),
120 #[cfg(feature = "quinn")]
121 Self::Quinn(err) => err.connect_error(),
122 #[cfg(feature = "noq")]
123 Self::Noq(err) => err.connect_error(),
124 #[cfg(feature = "quiche")]
125 Self::Quiche(err) => err.connect_error(),
126 #[cfg(feature = "websocket")]
127 Self::TransportRace { quic, websocket } => quic.connect_error().or_else(|| websocket.connect_error()),
128 #[cfg(feature = "websocket")]
129 Self::WebSocket(err) => err.connect_error(),
130 _ => None,
131 }
132 }
133
134 pub fn is_auth(&self) -> bool {
136 self.connect_error().is_some_and(|err| err.is_auth())
137 }
138}
139
140impl From<std::io::Error> for Error {
143 fn from(err: std::io::Error) -> Self {
144 Self::Io(Arc::new(err))
145 }
146}
147
148impl From<tracing_subscriber::filter::ParseError> for Error {
149 fn from(err: tracing_subscriber::filter::ParseError) -> Self {
150 Self::Directive(Arc::new(err))
151 }
152}
153
154impl From<crate::tls::Error> for Error {
155 fn from(err: crate::tls::Error) -> Self {
156 Self::Tls(Arc::new(err))
157 }
158}
159
160#[cfg(feature = "quinn")]
161impl From<crate::quinn::Error> for Error {
162 fn from(err: crate::quinn::Error) -> Self {
163 if let Some(err) = err.connect_error() {
164 return Self::Connect(err);
165 }
166
167 Self::Quinn(Arc::new(err))
168 }
169}
170
171#[cfg(feature = "noq")]
172impl From<crate::noq::Error> for Error {
173 fn from(err: crate::noq::Error) -> Self {
174 if let Some(err) = err.connect_error() {
175 return Self::Connect(err);
176 }
177
178 Self::Noq(Arc::new(err))
179 }
180}
181
182#[cfg(feature = "quiche")]
183impl From<crate::quiche::Error> for Error {
184 fn from(err: crate::quiche::Error) -> Self {
185 if let Some(err) = err.connect_error() {
186 return Self::Connect(err);
187 }
188
189 Self::Quiche(Arc::new(err))
190 }
191}
192
193#[cfg(feature = "iroh")]
194impl From<crate::iroh::Error> for Error {
195 fn from(err: crate::iroh::Error) -> Self {
196 Self::Iroh(Arc::new(err))
197 }
198}
199
200#[cfg(feature = "websocket")]
201impl From<crate::websocket::Error> for Error {
202 fn from(err: crate::websocket::Error) -> Self {
203 if let Some(err) = err.connect_error() {
204 return Self::Connect(err);
205 }
206
207 Self::WebSocket(Arc::new(err))
208 }
209}
210
211#[cfg(feature = "tcp")]
212impl From<crate::tcp::Error> for Error {
213 fn from(err: crate::tcp::Error) -> Self {
214 Self::Tcp(Arc::new(err))
215 }
216}
217
218#[cfg(all(feature = "uds", unix))]
219impl From<crate::unix::Error> for Error {
220 fn from(err: crate::unix::Error) -> Self {
221 Self::Unix(Arc::new(err))
222 }
223}
224
225pub type Result<T> = std::result::Result<T, Error>;
227
228#[cfg(all(test, feature = "websocket"))]
229mod tests {
230 use super::*;
231
232 #[test]
233 fn transport_race_propagates_nested_connect_errors() {
234 let quic = Error::TransportRace {
235 quic: Arc::new(crate::ConnectError::Unauthorized.into()),
236 websocket: Arc::new(crate::ConnectError::Forbidden.into()),
237 };
238 assert_eq!(quic.connect_error(), Some(crate::ConnectError::Unauthorized));
239
240 let websocket = Error::TransportRace {
241 quic: Arc::new(Error::ConnectFailed),
242 websocket: Arc::new(crate::ConnectError::Forbidden.into()),
243 };
244 assert_eq!(websocket.connect_error(), Some(crate::ConnectError::Forbidden));
245 }
246}