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
76impl Error {
77 pub fn connect_error(&self) -> Option<crate::ConnectError> {
78 match self {
79 Self::Connect(err) => Some(*err),
80 Self::MoqNet(moq_net::Error::Unauthorized) => Some(crate::ConnectError::Unauthorized),
81 #[cfg(feature = "quinn")]
82 Self::Quinn(err) => err.connect_error(),
83 #[cfg(feature = "noq")]
84 Self::Noq(err) => err.connect_error(),
85 #[cfg(feature = "quiche")]
86 Self::Quiche(err) => err.connect_error(),
87 #[cfg(feature = "websocket")]
88 Self::TransportRace { quic, websocket } => quic.connect_error().or_else(|| websocket.connect_error()),
89 #[cfg(feature = "websocket")]
90 Self::WebSocket(err) => err.connect_error(),
91 _ => None,
92 }
93 }
94
95 pub fn is_auth(&self) -> bool {
96 self.connect_error().is_some_and(|err| err.is_auth())
97 }
98}
99
100impl From<std::io::Error> for Error {
103 fn from(err: std::io::Error) -> Self {
104 Self::Io(Arc::new(err))
105 }
106}
107
108impl From<tracing_subscriber::filter::ParseError> for Error {
109 fn from(err: tracing_subscriber::filter::ParseError) -> Self {
110 Self::Directive(Arc::new(err))
111 }
112}
113
114impl From<crate::tls::Error> for Error {
115 fn from(err: crate::tls::Error) -> Self {
116 Self::Tls(Arc::new(err))
117 }
118}
119
120#[cfg(feature = "quinn")]
121impl From<crate::quinn::Error> for Error {
122 fn from(err: crate::quinn::Error) -> Self {
123 if let Some(err) = err.connect_error() {
124 return Self::Connect(err);
125 }
126
127 Self::Quinn(Arc::new(err))
128 }
129}
130
131#[cfg(feature = "noq")]
132impl From<crate::noq::Error> for Error {
133 fn from(err: crate::noq::Error) -> Self {
134 if let Some(err) = err.connect_error() {
135 return Self::Connect(err);
136 }
137
138 Self::Noq(Arc::new(err))
139 }
140}
141
142#[cfg(feature = "quiche")]
143impl From<crate::quiche::Error> for Error {
144 fn from(err: crate::quiche::Error) -> Self {
145 if let Some(err) = err.connect_error() {
146 return Self::Connect(err);
147 }
148
149 Self::Quiche(Arc::new(err))
150 }
151}
152
153#[cfg(feature = "iroh")]
154impl From<crate::iroh::Error> for Error {
155 fn from(err: crate::iroh::Error) -> Self {
156 Self::Iroh(Arc::new(err))
157 }
158}
159
160#[cfg(feature = "websocket")]
161impl From<crate::websocket::Error> for Error {
162 fn from(err: crate::websocket::Error) -> Self {
163 if let Some(err) = err.connect_error() {
164 return Self::Connect(err);
165 }
166
167 Self::WebSocket(Arc::new(err))
168 }
169}
170
171pub type Result<T> = std::result::Result<T, Error>;
173
174#[cfg(all(test, feature = "websocket"))]
175mod tests {
176 use super::*;
177
178 #[test]
179 fn transport_race_propagates_nested_connect_errors() {
180 let quic = Error::TransportRace {
181 quic: Arc::new(crate::ConnectError::Unauthorized.into()),
182 websocket: Arc::new(crate::ConnectError::Forbidden.into()),
183 };
184 assert_eq!(quic.connect_error(), Some(crate::ConnectError::Unauthorized));
185
186 let websocket = Error::TransportRace {
187 quic: Arc::new(Error::ConnectFailed),
188 websocket: Arc::new(crate::ConnectError::Forbidden.into()),
189 };
190 assert_eq!(websocket.connect_error(), Some(crate::ConnectError::Forbidden));
191 }
192}