1use std::sync::Arc;
2
3#[derive(Debug, Clone, thiserror::Error)]
9#[non_exhaustive]
10pub enum Error {
11 #[error(transparent)]
12 Io(Arc<std::io::Error>),
13
14 #[error(transparent)]
15 MoqNet(#[from] moq_net::Error),
16
17 #[error("invalid log directive")]
18 Directive(#[source] Arc<tracing_subscriber::filter::ParseError>),
19
20 #[error("failed to set global tracing subscriber")]
21 SetSubscriber(#[source] Arc<tracing_subscriber::util::TryInitError>),
22
23 #[error("failed to initialize Android logcat layer")]
24 Logcat(#[source] Arc<std::io::Error>),
25
26 #[error("{0}")]
27 NoBackend(&'static str),
28
29 #[error("failed to connect to server")]
30 ConnectFailed,
31
32 #[error(transparent)]
33 Connect(#[from] crate::ConnectError),
34
35 #[cfg(feature = "websocket")]
36 #[error("failed to connect to server: QUIC failed: {quic}; WebSocket failed: {websocket}")]
37 TransportRace { quic: Arc<Error>, websocket: Arc<Error> },
38
39 #[cfg(feature = "iroh")]
40 #[error("Iroh support is not enabled")]
41 IrohDisabled,
42
43 #[error("tls.root (mTLS) is not supported by the selected QUIC backend")]
44 MtlsUnsupported,
45
46 #[error("invalid status code")]
47 InvalidStatusCode,
48
49 #[error("{0}")]
50 Reconnect(String),
51
52 #[error(transparent)]
53 Tls(Arc<crate::tls::Error>),
54
55 #[cfg(feature = "quinn")]
56 #[error(transparent)]
57 Quinn(Arc<crate::quinn::Error>),
58
59 #[cfg(feature = "noq")]
60 #[error(transparent)]
61 Noq(Arc<crate::noq::Error>),
62
63 #[cfg(feature = "quiche")]
64 #[error(transparent)]
65 Quiche(Arc<crate::quiche::Error>),
66
67 #[cfg(feature = "iroh")]
68 #[error(transparent)]
69 Iroh(Arc<crate::iroh::Error>),
70
71 #[cfg(feature = "websocket")]
72 #[error(transparent)]
73 WebSocket(Arc<crate::websocket::Error>),
74
75 #[cfg(feature = "tcp")]
76 #[error(transparent)]
77 Tcp(Arc<crate::tcp::Error>),
78
79 #[cfg(all(feature = "uds", unix))]
80 #[error(transparent)]
81 Unix(Arc<crate::unix::Error>),
82}
83
84impl Error {
85 pub fn connect_error(&self) -> Option<crate::ConnectError> {
86 match self {
87 Self::Connect(err) => Some(*err),
88 Self::MoqNet(moq_net::Error::Unauthorized) => Some(crate::ConnectError::Unauthorized),
89 #[cfg(feature = "quinn")]
90 Self::Quinn(err) => err.connect_error(),
91 #[cfg(feature = "noq")]
92 Self::Noq(err) => err.connect_error(),
93 #[cfg(feature = "quiche")]
94 Self::Quiche(err) => err.connect_error(),
95 #[cfg(feature = "websocket")]
96 Self::TransportRace { quic, websocket } => quic.connect_error().or_else(|| websocket.connect_error()),
97 #[cfg(feature = "websocket")]
98 Self::WebSocket(err) => err.connect_error(),
99 _ => None,
100 }
101 }
102
103 pub fn is_auth(&self) -> bool {
104 self.connect_error().is_some_and(|err| err.is_auth())
105 }
106}
107
108impl From<std::io::Error> for Error {
111 fn from(err: std::io::Error) -> Self {
112 Self::Io(Arc::new(err))
113 }
114}
115
116impl From<tracing_subscriber::filter::ParseError> for Error {
117 fn from(err: tracing_subscriber::filter::ParseError) -> Self {
118 Self::Directive(Arc::new(err))
119 }
120}
121
122impl From<crate::tls::Error> for Error {
123 fn from(err: crate::tls::Error) -> Self {
124 Self::Tls(Arc::new(err))
125 }
126}
127
128#[cfg(feature = "quinn")]
129impl From<crate::quinn::Error> for Error {
130 fn from(err: crate::quinn::Error) -> Self {
131 if let Some(err) = err.connect_error() {
132 return Self::Connect(err);
133 }
134
135 Self::Quinn(Arc::new(err))
136 }
137}
138
139#[cfg(feature = "noq")]
140impl From<crate::noq::Error> for Error {
141 fn from(err: crate::noq::Error) -> Self {
142 if let Some(err) = err.connect_error() {
143 return Self::Connect(err);
144 }
145
146 Self::Noq(Arc::new(err))
147 }
148}
149
150#[cfg(feature = "quiche")]
151impl From<crate::quiche::Error> for Error {
152 fn from(err: crate::quiche::Error) -> Self {
153 if let Some(err) = err.connect_error() {
154 return Self::Connect(err);
155 }
156
157 Self::Quiche(Arc::new(err))
158 }
159}
160
161#[cfg(feature = "iroh")]
162impl From<crate::iroh::Error> for Error {
163 fn from(err: crate::iroh::Error) -> Self {
164 Self::Iroh(Arc::new(err))
165 }
166}
167
168#[cfg(feature = "websocket")]
169impl From<crate::websocket::Error> for Error {
170 fn from(err: crate::websocket::Error) -> Self {
171 if let Some(err) = err.connect_error() {
172 return Self::Connect(err);
173 }
174
175 Self::WebSocket(Arc::new(err))
176 }
177}
178
179#[cfg(feature = "tcp")]
180impl From<crate::tcp::Error> for Error {
181 fn from(err: crate::tcp::Error) -> Self {
182 Self::Tcp(Arc::new(err))
183 }
184}
185
186#[cfg(all(feature = "uds", unix))]
187impl From<crate::unix::Error> for Error {
188 fn from(err: crate::unix::Error) -> Self {
189 Self::Unix(Arc::new(err))
190 }
191}
192
193pub type Result<T> = std::result::Result<T, Error>;
195
196#[cfg(all(test, feature = "websocket"))]
197mod tests {
198 use super::*;
199
200 #[test]
201 fn transport_race_propagates_nested_connect_errors() {
202 let quic = Error::TransportRace {
203 quic: Arc::new(crate::ConnectError::Unauthorized.into()),
204 websocket: Arc::new(crate::ConnectError::Forbidden.into()),
205 };
206 assert_eq!(quic.connect_error(), Some(crate::ConnectError::Unauthorized));
207
208 let websocket = Error::TransportRace {
209 quic: Arc::new(Error::ConnectFailed),
210 websocket: Arc::new(crate::ConnectError::Forbidden.into()),
211 };
212 assert_eq!(websocket.connect_error(), Some(crate::ConnectError::Forbidden));
213 }
214}