veilid_tools/
network_result.rs

1use super::*;
2
3use core::fmt::{Debug, Display};
4use core::result::Result;
5use std::error::Error;
6use std::io;
7
8//////////////////////////////////////////////////////////////////
9// Non-fallible network results conversions
10
11pub trait NetworkResultExt<T> {
12    fn into_network_result(self) -> NetworkResult<T>;
13}
14
15impl<T> NetworkResultExt<T> for Result<T, TimeoutError> {
16    fn into_network_result(self) -> NetworkResult<T> {
17        self.ok()
18            .map(|v| NetworkResult::<T>::Value(v))
19            .unwrap_or(NetworkResult::<T>::Timeout)
20    }
21}
22
23pub trait IoNetworkResultExt<T> {
24    fn into_network_result(self) -> io::Result<NetworkResult<T>>;
25}
26
27fn io_error_kind_from_error<T>(e: io::Error) -> io::Result<NetworkResult<T>> {
28    #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
29    if let Some(os_err) = e.raw_os_error() {
30        if os_err == libc::EHOSTUNREACH || os_err == libc::ENETUNREACH {
31            return Ok(NetworkResult::NoConnection(e));
32        }
33    }
34    #[cfg(windows)]
35    if let Some(os_err) = e.raw_os_error() {
36        if os_err == winapi::um::winsock2::WSAENETRESET
37            || os_err == winapi::um::winsock2::WSAENETUNREACH
38        {
39            return Ok(NetworkResult::NoConnection(e));
40        }
41    }
42    match e.kind() {
43        io::ErrorKind::TimedOut => Ok(NetworkResult::Timeout),
44        io::ErrorKind::UnexpectedEof
45        | io::ErrorKind::NotConnected
46        | io::ErrorKind::BrokenPipe
47        | io::ErrorKind::ConnectionAborted
48        | io::ErrorKind::ConnectionRefused
49        | io::ErrorKind::ConnectionReset => Ok(NetworkResult::NoConnection(e)),
50        io::ErrorKind::InvalidInput | io::ErrorKind::InvalidData => {
51            Ok(NetworkResult::InvalidMessage(e.to_string()))
52        }
53        io::ErrorKind::AddrNotAvailable | io::ErrorKind::AddrInUse => {
54            Ok(NetworkResult::AlreadyExists(e))
55        }
56        _ => Err(e),
57    }
58}
59
60impl<T> IoNetworkResultExt<T> for io::Result<T> {
61    fn into_network_result(self) -> io::Result<NetworkResult<T>> {
62        match self {
63            Ok(v) => Ok(NetworkResult::Value(v)),
64            Err(e) => io_error_kind_from_error(e),
65        }
66    }
67}
68
69pub trait NetworkResultResultExt<T, E> {
70    fn into_result_network_result(self) -> Result<NetworkResult<T>, E>;
71}
72
73impl<T, E> NetworkResultResultExt<T, E> for NetworkResult<Result<T, E>> {
74    fn into_result_network_result(self) -> Result<NetworkResult<T>, E> {
75        match self {
76            NetworkResult::Timeout => Ok(NetworkResult::<T>::Timeout),
77            NetworkResult::ServiceUnavailable(s) => Ok(NetworkResult::<T>::ServiceUnavailable(s)),
78            NetworkResult::NoConnection(e) => Ok(NetworkResult::<T>::NoConnection(e)),
79            NetworkResult::AlreadyExists(e) => Ok(NetworkResult::<T>::AlreadyExists(e)),
80            NetworkResult::InvalidMessage(s) => Ok(NetworkResult::<T>::InvalidMessage(s)),
81            NetworkResult::Value(Ok(v)) => Ok(NetworkResult::<T>::Value(v)),
82            NetworkResult::Value(Err(e)) => Err(e),
83        }
84    }
85}
86
87pub trait FoldedNetworkResultExt<T> {
88    fn folded(self) -> io::Result<NetworkResult<T>>;
89}
90
91impl<T> FoldedNetworkResultExt<T> for io::Result<TimeoutOr<T>> {
92    fn folded(self) -> io::Result<NetworkResult<T>> {
93        match self {
94            Ok(TimeoutOr::Timeout) => Ok(NetworkResult::Timeout),
95            Ok(TimeoutOr::Value(v)) => Ok(NetworkResult::Value(v)),
96            Err(e) => io_error_kind_from_error(e),
97        }
98    }
99}
100
101impl<T> FoldedNetworkResultExt<T> for io::Result<NetworkResult<T>> {
102    fn folded(self) -> io::Result<NetworkResult<T>> {
103        match self {
104            Ok(v) => Ok(v),
105            Err(e) => io_error_kind_from_error(e),
106        }
107    }
108}
109
110//////////////////////////////////////////////////////////////////
111// Non-fallible network result
112
113#[must_use]
114pub enum NetworkResult<T> {
115    Timeout,
116    ServiceUnavailable(String),
117    NoConnection(io::Error),
118    AlreadyExists(io::Error),
119    InvalidMessage(String),
120    Value(T),
121}
122
123impl<T> NetworkResult<T> {
124    pub fn timeout() -> Self {
125        Self::Timeout
126    }
127    pub fn service_unavailable<S: ToString>(s: S) -> Self {
128        Self::ServiceUnavailable(s.to_string())
129    }
130    pub fn no_connection(e: io::Error) -> Self {
131        Self::NoConnection(e)
132    }
133    pub fn no_connection_other<S: ToString>(s: S) -> Self {
134        Self::NoConnection(io::Error::new(io::ErrorKind::Other, s.to_string()))
135    }
136    pub fn invalid_message<S: ToString>(s: S) -> Self {
137        Self::InvalidMessage(s.to_string())
138    }
139    pub fn already_exists(e: io::Error) -> Self {
140        Self::AlreadyExists(e)
141    }
142    pub fn value(value: T) -> Self {
143        Self::Value(value)
144    }
145
146    pub fn is_timeout(&self) -> bool {
147        matches!(self, Self::Timeout)
148    }
149    pub fn is_no_connection(&self) -> bool {
150        matches!(self, Self::NoConnection(_))
151    }
152    pub fn is_already_exists(&self) -> bool {
153        matches!(self, Self::AlreadyExists(_))
154    }
155    pub fn is_invalid_message(&self) -> bool {
156        matches!(self, Self::InvalidMessage(_))
157    }
158    pub fn is_value(&self) -> bool {
159        matches!(self, Self::Value(_))
160    }
161    pub fn map<X, F: Fn(T) -> X>(self, f: F) -> NetworkResult<X> {
162        match self {
163            Self::Timeout => NetworkResult::<X>::Timeout,
164            Self::ServiceUnavailable(s) => NetworkResult::<X>::ServiceUnavailable(s),
165            Self::NoConnection(e) => NetworkResult::<X>::NoConnection(e),
166            Self::AlreadyExists(e) => NetworkResult::<X>::AlreadyExists(e),
167            Self::InvalidMessage(s) => NetworkResult::<X>::InvalidMessage(s),
168            Self::Value(v) => NetworkResult::<X>::Value(f(v)),
169        }
170    }
171    pub fn into_io_result(self) -> Result<T, io::Error> {
172        match self {
173            Self::Timeout => Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out")),
174            Self::ServiceUnavailable(s) => Err(io::Error::new(
175                io::ErrorKind::NotFound,
176                format!("Service unavailable: {}", s),
177            )),
178            Self::NoConnection(e) => Err(e),
179            Self::AlreadyExists(e) => Err(e),
180            Self::InvalidMessage(s) => Err(io::Error::new(
181                io::ErrorKind::InvalidData,
182                format!("Invalid message: {}", s),
183            )),
184            Self::Value(v) => Ok(v),
185        }
186    }
187}
188
189impl<T> From<NetworkResult<T>> for Option<T> {
190    fn from(t: NetworkResult<T>) -> Self {
191        match t {
192            NetworkResult::Value(v) => Some(v),
193            _ => None,
194        }
195    }
196}
197
198impl<T: Debug> Debug for NetworkResult<T> {
199    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
200        match self {
201            Self::Timeout => write!(f, "Timeout"),
202            Self::ServiceUnavailable(s) => f.debug_tuple("ServiceUnavailable").field(s).finish(),
203            Self::NoConnection(e) => f.debug_tuple("NoConnection").field(e).finish(),
204            Self::AlreadyExists(e) => f.debug_tuple("AlreadyExists").field(e).finish(),
205            Self::InvalidMessage(s) => f.debug_tuple("InvalidMessage").field(s).finish(),
206            Self::Value(v) => f.debug_tuple("Value").field(v).finish(),
207        }
208    }
209}
210
211impl<T> Display for NetworkResult<T> {
212    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
213        match self {
214            Self::Timeout => write!(f, "Timeout"),
215            Self::ServiceUnavailable(s) => write!(f, "ServiceUnavailable({})", s),
216            Self::NoConnection(e) => write!(f, "NoConnection({})", e.kind()),
217            Self::AlreadyExists(e) => write!(f, "AlreadyExists({})", e.kind()),
218            Self::InvalidMessage(s) => write!(f, "InvalidMessage({})", s),
219            Self::Value(_) => write!(f, "Value"),
220        }
221    }
222}
223
224impl<T: Debug + Display> Error for NetworkResult<T> {}
225
226//////////////////////////////////////////////////////////////////
227// Non-fallible network result macros
228
229#[macro_export]
230macro_rules! network_result_raise {
231    ($r: expr) => {
232        match $r {
233            NetworkResult::Timeout => return Ok(NetworkResult::Timeout),
234            NetworkResult::ServiceUnavailable(s) => return Ok(NetworkResult::ServiceUnavailable(s)),
235            NetworkResult::NoConnection(e) => return Ok(NetworkResult::NoConnection(e)),
236            NetworkResult::AlreadyExists(e) => return Ok(NetworkResult::AlreadyExists(e)),
237            NetworkResult::InvalidMessage(s) => return Ok(NetworkResult::InvalidMessage(s)),
238            NetworkResult::Value(_) => panic!("Can not raise value"),
239        }
240    };
241}
242
243#[macro_export]
244macro_rules! network_result_try {
245    ($r: expr) => {
246        match $r {
247            NetworkResult::Timeout => return Ok(NetworkResult::Timeout),
248            NetworkResult::ServiceUnavailable(s) => return Ok(NetworkResult::ServiceUnavailable(s)),
249            NetworkResult::NoConnection(e) => return Ok(NetworkResult::NoConnection(e)),
250            NetworkResult::AlreadyExists(e) => return Ok(NetworkResult::AlreadyExists(e)),
251            NetworkResult::InvalidMessage(s) => return Ok(NetworkResult::InvalidMessage(s)),
252            NetworkResult::Value(v) => v,
253        }
254    };
255    ($r:expr => $f:tt) => {
256        match $r {
257            NetworkResult::Timeout => {
258                $f;
259                return Ok(NetworkResult::Timeout);
260            }
261            NetworkResult::ServiceUnavailable(s) => {
262                $f;
263                return Ok(NetworkResult::ServiceUnavailable(s));
264            }
265            NetworkResult::NoConnection(e) => {
266                $f;
267                return Ok(NetworkResult::NoConnection(e));
268            }
269            NetworkResult::AlreadyExists(e) => {
270                $f;
271                return Ok(NetworkResult::AlreadyExists(e));
272            }
273            NetworkResult::InvalidMessage(s) => {
274                $f;
275                return Ok(NetworkResult::InvalidMessage(s));
276            }
277            NetworkResult::Value(v) => v,
278        }
279    };
280}
281
282#[macro_export]
283macro_rules! network_result_value_or_log {
284    ($self:ident $r:expr => $f:expr) => {
285        network_result_value_or_log!($self $r => [ "" ] $f )
286    };
287    ($self:ident $r:expr => [ $d:expr ] $f:expr) => { {
288        let __extra_message = if debug_target_enabled!("network_result") {
289            $d.to_string()
290        } else {
291            "".to_string()
292        };
293        match $r {
294            NetworkResult::Timeout => {
295                veilid_log!($self debug
296                    "{} at {}@{}:{} in {}{}",
297                    "Timeout",
298                    file!(),
299                    line!(),
300                    column!(),
301                    fn_name::uninstantiated!(),
302                    __extra_message
303                );
304                $f
305            }
306            NetworkResult::ServiceUnavailable(ref s) => {
307                veilid_log!($self debug
308                    "{}({}) at {}@{}:{} in {}{}",
309                    "ServiceUnavailable",
310                    s,
311                    file!(),
312                    line!(),
313                    column!(),
314                    fn_name::uninstantiated!(),
315                    __extra_message
316                );
317                $f
318            }
319            NetworkResult::NoConnection(ref e) => {
320                veilid_log!($self debug
321                    "{}({}) at {}@{}:{} in {}{}",
322                    "No connection",
323                    e.to_string(),
324                    file!(),
325                    line!(),
326                    column!(),
327                    fn_name::uninstantiated!(),
328                    __extra_message
329                );
330                $f
331            }
332            NetworkResult::AlreadyExists(ref e) => {
333                veilid_log!($self debug
334                    "{}({}) at {}@{}:{} in {}{}",
335                    "Already exists",
336                    e.to_string(),
337                    file!(),
338                    line!(),
339                    column!(),
340                    fn_name::uninstantiated!(),
341                    __extra_message
342                );
343                $f
344            }
345            NetworkResult::InvalidMessage(ref s) => {
346                veilid_log!($self debug
347                    "{}({}) at {}@{}:{} in {}{}",
348                    "Invalid message",
349                    s,
350                    file!(),
351                    line!(),
352                    column!(),
353                    fn_name::uninstantiated!(),
354                    __extra_message
355                );
356                $f
357            }
358            NetworkResult::Value(v) => v,
359        }
360    } };
361
362}