Skip to main content

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
50        | io::ErrorKind::InvalidInput => Ok(NetworkResult::NoConnection(e)),
51        io::ErrorKind::InvalidData => Ok(NetworkResult::InvalidMessage(e.to_string())),
52        io::ErrorKind::AddrNotAvailable | io::ErrorKind::AddrInUse => {
53            Ok(NetworkResult::AlreadyExists(e))
54        }
55        _ => Err(e),
56    }
57}
58
59impl<T> IoNetworkResultExt<T> for io::Result<T> {
60    fn into_network_result(self) -> io::Result<NetworkResult<T>> {
61        match self {
62            Ok(v) => Ok(NetworkResult::Value(v)),
63            Err(e) => io_error_kind_from_error(e),
64        }
65    }
66}
67
68pub trait NetworkResultResultExt<T, E> {
69    fn into_result_network_result(self) -> Result<NetworkResult<T>, E>;
70}
71
72impl<T, E> NetworkResultResultExt<T, E> for NetworkResult<Result<T, E>> {
73    fn into_result_network_result(self) -> Result<NetworkResult<T>, E> {
74        match self {
75            NetworkResult::Timeout => Ok(NetworkResult::<T>::Timeout),
76            NetworkResult::ServiceUnavailable(s) => Ok(NetworkResult::<T>::ServiceUnavailable(s)),
77            NetworkResult::NoConnection(e) => Ok(NetworkResult::<T>::NoConnection(e)),
78            NetworkResult::AlreadyExists(e) => Ok(NetworkResult::<T>::AlreadyExists(e)),
79            NetworkResult::InvalidMessage(s) => Ok(NetworkResult::<T>::InvalidMessage(s)),
80            NetworkResult::Value(Ok(v)) => Ok(NetworkResult::<T>::Value(v)),
81            NetworkResult::Value(Err(e)) => Err(e),
82        }
83    }
84}
85
86pub trait FoldedNetworkResultExt<T> {
87    fn folded(self) -> io::Result<NetworkResult<T>>;
88}
89
90impl<T> FoldedNetworkResultExt<T> for io::Result<TimeoutOr<T>> {
91    fn folded(self) -> io::Result<NetworkResult<T>> {
92        match self {
93            Ok(TimeoutOr::Timeout) => Ok(NetworkResult::Timeout),
94            Ok(TimeoutOr::Value(v)) => Ok(NetworkResult::Value(v)),
95            Err(e) => io_error_kind_from_error(e),
96        }
97    }
98}
99
100impl<T> FoldedNetworkResultExt<T> for io::Result<NetworkResult<T>> {
101    fn folded(self) -> io::Result<NetworkResult<T>> {
102        match self {
103            Ok(v) => Ok(v),
104            Err(e) => io_error_kind_from_error(e),
105        }
106    }
107}
108
109//////////////////////////////////////////////////////////////////
110// Non-fallible network result
111
112#[must_use]
113pub enum NetworkResult<T> {
114    Timeout,
115    ServiceUnavailable(String),
116    NoConnection(io::Error),
117    AlreadyExists(io::Error),
118    InvalidMessage(String),
119    Value(T),
120}
121
122impl<T> NetworkResult<T> {
123    pub fn timeout() -> Self {
124        Self::Timeout
125    }
126    pub fn service_unavailable<S: ToString>(s: S) -> Self {
127        Self::ServiceUnavailable(s.to_string())
128    }
129    pub fn no_connection(e: io::Error) -> Self {
130        Self::NoConnection(e)
131    }
132    pub fn no_connection_other<S: ToString>(s: S) -> Self {
133        Self::NoConnection(io::Error::other(s.to_string()))
134    }
135    pub fn invalid_message<S: ToString>(s: S) -> Self {
136        Self::InvalidMessage(s.to_string())
137    }
138    pub fn already_exists(e: io::Error) -> Self {
139        Self::AlreadyExists(e)
140    }
141    pub fn value(value: T) -> Self {
142        Self::Value(value)
143    }
144
145    pub fn is_timeout(&self) -> bool {
146        matches!(self, Self::Timeout)
147    }
148    pub fn is_no_connection(&self) -> bool {
149        matches!(self, Self::NoConnection(_))
150    }
151    pub fn is_already_exists(&self) -> bool {
152        matches!(self, Self::AlreadyExists(_))
153    }
154    pub fn is_invalid_message(&self) -> bool {
155        matches!(self, Self::InvalidMessage(_))
156    }
157    pub fn is_value(&self) -> bool {
158        matches!(self, Self::Value(_))
159    }
160    pub fn map<X, F: Fn(T) -> X>(self, f: F) -> NetworkResult<X> {
161        match self {
162            Self::Timeout => NetworkResult::<X>::Timeout,
163            Self::ServiceUnavailable(s) => NetworkResult::<X>::ServiceUnavailable(s),
164            Self::NoConnection(e) => NetworkResult::<X>::NoConnection(e),
165            Self::AlreadyExists(e) => NetworkResult::<X>::AlreadyExists(e),
166            Self::InvalidMessage(s) => NetworkResult::<X>::InvalidMessage(s),
167            Self::Value(v) => NetworkResult::<X>::Value(f(v)),
168        }
169    }
170    pub fn into_io_result(self) -> Result<T, io::Error> {
171        match self {
172            Self::Timeout => Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out")),
173            Self::ServiceUnavailable(s) => Err(io::Error::new(
174                io::ErrorKind::NotFound,
175                format!("Service unavailable: {}", s),
176            )),
177            Self::NoConnection(e) => Err(e),
178            Self::AlreadyExists(e) => Err(e),
179            Self::InvalidMessage(s) => Err(io::Error::new(
180                io::ErrorKind::InvalidData,
181                format!("Invalid message: {}", s),
182            )),
183            Self::Value(v) => Ok(v),
184        }
185    }
186}
187
188impl<T> From<NetworkResult<T>> for Option<T> {
189    fn from(t: NetworkResult<T>) -> Self {
190        match t {
191            NetworkResult::Value(v) => Some(v),
192            _ => None,
193        }
194    }
195}
196
197impl<T: Debug> Debug for NetworkResult<T> {
198    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
199        match self {
200            Self::Timeout => write!(f, "Timeout"),
201            Self::ServiceUnavailable(s) => f.debug_tuple("ServiceUnavailable").field(s).finish(),
202            Self::NoConnection(e) => f.debug_tuple("NoConnection").field(e).finish(),
203            Self::AlreadyExists(e) => f.debug_tuple("AlreadyExists").field(e).finish(),
204            Self::InvalidMessage(s) => f.debug_tuple("InvalidMessage").field(s).finish(),
205            Self::Value(v) => f.debug_tuple("Value").field(v).finish(),
206        }
207    }
208}
209
210impl<T> Display for NetworkResult<T> {
211    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
212        match self {
213            Self::Timeout => write!(f, "Timeout"),
214            Self::ServiceUnavailable(s) => write!(f, "ServiceUnavailable({})", s),
215            Self::NoConnection(e) => write!(f, "NoConnection({})", e.kind()),
216            Self::AlreadyExists(e) => write!(f, "AlreadyExists({})", e.kind()),
217            Self::InvalidMessage(s) => write!(f, "InvalidMessage({})", s),
218            Self::Value(_) => write!(f, "Value"),
219        }
220    }
221}
222
223impl<T: Debug + Display> Error for NetworkResult<T> {}
224
225//////////////////////////////////////////////////////////////////
226// Non-fallible network result macros
227
228#[macro_export]
229macro_rules! network_result_raise {
230    ($r: expr) => {
231        match $r {
232            NetworkResult::Timeout => return Ok(NetworkResult::Timeout),
233            NetworkResult::ServiceUnavailable(s) => return Ok(NetworkResult::ServiceUnavailable(s)),
234            NetworkResult::NoConnection(e) => return Ok(NetworkResult::NoConnection(e)),
235            NetworkResult::AlreadyExists(e) => return Ok(NetworkResult::AlreadyExists(e)),
236            NetworkResult::InvalidMessage(s) => return Ok(NetworkResult::InvalidMessage(s)),
237            NetworkResult::Value(_) => panic!("Can not raise value"),
238        }
239    };
240}
241
242#[macro_export]
243macro_rules! network_result_try {
244    ($r: expr) => {
245        match $r {
246            NetworkResult::Timeout => return Ok(NetworkResult::Timeout),
247            NetworkResult::ServiceUnavailable(s) => return Ok(NetworkResult::ServiceUnavailable(s)),
248            NetworkResult::NoConnection(e) => return Ok(NetworkResult::NoConnection(e)),
249            NetworkResult::AlreadyExists(e) => return Ok(NetworkResult::AlreadyExists(e)),
250            NetworkResult::InvalidMessage(s) => return Ok(NetworkResult::InvalidMessage(s)),
251            NetworkResult::Value(v) => v,
252        }
253    };
254    ($r:expr => $f:tt) => {
255        match $r {
256            NetworkResult::Timeout => {
257                $f;
258                return Ok(NetworkResult::Timeout);
259            }
260            NetworkResult::ServiceUnavailable(s) => {
261                $f;
262                return Ok(NetworkResult::ServiceUnavailable(s));
263            }
264            NetworkResult::NoConnection(e) => {
265                $f;
266                return Ok(NetworkResult::NoConnection(e));
267            }
268            NetworkResult::AlreadyExists(e) => {
269                $f;
270                return Ok(NetworkResult::AlreadyExists(e));
271            }
272            NetworkResult::InvalidMessage(s) => {
273                $f;
274                return Ok(NetworkResult::InvalidMessage(s));
275            }
276            NetworkResult::Value(v) => v,
277        }
278    };
279}