1use core::ffi::c_int;
2use std::error;
3use std::fmt;
4use std::result;
5
6use crate::utils::c_string_as_str_unchecked;
7
8macro_rules! ares_errors {
9 ($($(#[$attr:meta])* $variant:ident => $ares:ident,)*) => {
10 #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, PartialOrd, Ord)]
12 pub enum Error {
13 $(
14 $(#[$attr])*
15 $variant = c_ares_sys::ares_status_t::$ares as isize,
16 )*
17 UNKNOWN,
19 }
20
21 impl From<i32> for Error {
22 fn from(code: i32) -> Self {
23 match code {
24 $(x if x == Error::$variant as i32 => Error::$variant,)*
25 _ => Error::UNKNOWN,
26 }
27 }
28 }
29
30 impl TryFrom<c_ares_sys::ares_status_t> for Error {
31 type Error = ();
32
33 fn try_from(
34 status: c_ares_sys::ares_status_t,
35 ) -> std::result::Result<Self, ()> {
36 match status {
37 c_ares_sys::ares_status_t::ARES_SUCCESS => Err(()),
38 $(c_ares_sys::ares_status_t::$ares => Ok(Error::$variant),)*
39 }
40 }
41 }
42 };
43}
44
45ares_errors! {
46 ENODATA => ARES_ENODATA,
48 EFORMERR => ARES_EFORMERR,
50 ESERVFAIL => ARES_ESERVFAIL,
52 ENOTFOUND => ARES_ENOTFOUND,
54 ENOTIMP => ARES_ENOTIMP,
56 EREFUSED => ARES_EREFUSED,
58 EBADQUERY => ARES_EBADQUERY,
60 EBADNAME => ARES_EBADNAME,
62 EBADFAMILY => ARES_EBADFAMILY,
64 EBADRESP => ARES_EBADRESP,
66 ECONNREFUSED => ARES_ECONNREFUSED,
68 ETIMEOUT => ARES_ETIMEOUT,
70 EOF => ARES_EOF,
72 EFILE => ARES_EFILE,
74 ENOMEM => ARES_ENOMEM,
76 EDESTRUCTION => ARES_EDESTRUCTION,
78 EBADSTR => ARES_EBADSTR,
80 EBADFLAGS => ARES_EBADFLAGS,
82 ENONAME => ARES_ENONAME,
84 EBADHINTS => ARES_EBADHINTS,
86 ENOTINITIALIZED => ARES_ENOTINITIALIZED,
88 ELOADIPHLPAPI => ARES_ELOADIPHLPAPI,
90 EADDRGETNETWORKPARAMS => ARES_EADDRGETNETWORKPARAMS,
92 ECANCELLED => ARES_ECANCELLED,
94 ESERVICE => ARES_ESERVICE,
96 ENOSERVER => ARES_ENOSERVER,
98}
99
100impl error::Error for Error {}
101
102impl fmt::Display for Error {
103 fn fmt(&self, fmt: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
104 let text = unsafe {
105 let ptr = c_ares_sys::ares_strerror(*self as c_int);
106 c_string_as_str_unchecked(ptr)
107 };
108 fmt.write_str(text)
109 }
110}
111
112pub type Result<T> = result::Result<T, Error>;
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use std::collections::HashSet;
119
120 #[test]
121 fn from_i32_known_codes() {
122 assert_eq!(Error::from(1), Error::ENODATA);
123 assert_eq!(Error::from(2), Error::EFORMERR);
124 assert_eq!(Error::from(3), Error::ESERVFAIL);
125 assert_eq!(Error::from(4), Error::ENOTFOUND);
126 assert_eq!(Error::from(5), Error::ENOTIMP);
127 assert_eq!(Error::from(6), Error::EREFUSED);
128 }
129
130 #[test]
131 fn from_i32_all_known_codes() {
132 assert_eq!(Error::from(Error::EBADQUERY as i32), Error::EBADQUERY);
133 assert_eq!(Error::from(Error::EBADNAME as i32), Error::EBADNAME);
134 assert_eq!(Error::from(Error::EBADFAMILY as i32), Error::EBADFAMILY);
135 assert_eq!(Error::from(Error::EBADRESP as i32), Error::EBADRESP);
136 assert_eq!(Error::from(Error::ECONNREFUSED as i32), Error::ECONNREFUSED);
137 assert_eq!(Error::from(Error::ETIMEOUT as i32), Error::ETIMEOUT);
138 assert_eq!(Error::from(Error::EOF as i32), Error::EOF);
139 assert_eq!(Error::from(Error::EFILE as i32), Error::EFILE);
140 assert_eq!(Error::from(Error::ENOMEM as i32), Error::ENOMEM);
141 assert_eq!(Error::from(Error::EDESTRUCTION as i32), Error::EDESTRUCTION);
142 assert_eq!(Error::from(Error::EBADSTR as i32), Error::EBADSTR);
143 assert_eq!(Error::from(Error::EBADFLAGS as i32), Error::EBADFLAGS);
144 assert_eq!(Error::from(Error::ENONAME as i32), Error::ENONAME);
145 assert_eq!(Error::from(Error::EBADHINTS as i32), Error::EBADHINTS);
146 assert_eq!(
147 Error::from(Error::ENOTINITIALIZED as i32),
148 Error::ENOTINITIALIZED
149 );
150 assert_eq!(
151 Error::from(Error::ELOADIPHLPAPI as i32),
152 Error::ELOADIPHLPAPI
153 );
154 assert_eq!(
155 Error::from(Error::EADDRGETNETWORKPARAMS as i32),
156 Error::EADDRGETNETWORKPARAMS
157 );
158 assert_eq!(Error::from(Error::ECANCELLED as i32), Error::ECANCELLED);
159 assert_eq!(Error::from(Error::ESERVICE as i32), Error::ESERVICE);
160 assert_eq!(Error::from(Error::ENOSERVER as i32), Error::ENOSERVER);
161 }
162
163 #[test]
164 fn from_i32_unknown_code() {
165 assert_eq!(Error::from(9999), Error::UNKNOWN);
166 assert_eq!(Error::from(-9999), Error::UNKNOWN);
167 }
168
169 #[test]
170 #[allow(clippy::too_many_lines)] fn try_from_status() {
172 assert!(Error::try_from(c_ares_sys::ares_status_t::ARES_SUCCESS).is_err());
173 assert_eq!(
174 Error::try_from(c_ares_sys::ares_status_t::ARES_ENODATA),
175 Ok(Error::ENODATA)
176 );
177 assert_eq!(
178 Error::try_from(c_ares_sys::ares_status_t::ARES_EFORMERR),
179 Ok(Error::EFORMERR)
180 );
181 assert_eq!(
182 Error::try_from(c_ares_sys::ares_status_t::ARES_ESERVFAIL),
183 Ok(Error::ESERVFAIL)
184 );
185 assert_eq!(
186 Error::try_from(c_ares_sys::ares_status_t::ARES_ENOTFOUND),
187 Ok(Error::ENOTFOUND)
188 );
189 assert_eq!(
190 Error::try_from(c_ares_sys::ares_status_t::ARES_ENOTIMP),
191 Ok(Error::ENOTIMP)
192 );
193 assert_eq!(
194 Error::try_from(c_ares_sys::ares_status_t::ARES_EREFUSED),
195 Ok(Error::EREFUSED)
196 );
197 assert_eq!(
198 Error::try_from(c_ares_sys::ares_status_t::ARES_EBADQUERY),
199 Ok(Error::EBADQUERY)
200 );
201 assert_eq!(
202 Error::try_from(c_ares_sys::ares_status_t::ARES_EBADNAME),
203 Ok(Error::EBADNAME)
204 );
205 assert_eq!(
206 Error::try_from(c_ares_sys::ares_status_t::ARES_EBADFAMILY),
207 Ok(Error::EBADFAMILY)
208 );
209 assert_eq!(
210 Error::try_from(c_ares_sys::ares_status_t::ARES_EBADRESP),
211 Ok(Error::EBADRESP)
212 );
213 assert_eq!(
214 Error::try_from(c_ares_sys::ares_status_t::ARES_ECONNREFUSED),
215 Ok(Error::ECONNREFUSED)
216 );
217 assert_eq!(
218 Error::try_from(c_ares_sys::ares_status_t::ARES_ETIMEOUT),
219 Ok(Error::ETIMEOUT)
220 );
221 assert_eq!(
222 Error::try_from(c_ares_sys::ares_status_t::ARES_EOF),
223 Ok(Error::EOF)
224 );
225 assert_eq!(
226 Error::try_from(c_ares_sys::ares_status_t::ARES_EFILE),
227 Ok(Error::EFILE)
228 );
229 assert_eq!(
230 Error::try_from(c_ares_sys::ares_status_t::ARES_ENOMEM),
231 Ok(Error::ENOMEM)
232 );
233 assert_eq!(
234 Error::try_from(c_ares_sys::ares_status_t::ARES_EDESTRUCTION),
235 Ok(Error::EDESTRUCTION)
236 );
237 assert_eq!(
238 Error::try_from(c_ares_sys::ares_status_t::ARES_EBADSTR),
239 Ok(Error::EBADSTR)
240 );
241 assert_eq!(
242 Error::try_from(c_ares_sys::ares_status_t::ARES_EBADFLAGS),
243 Ok(Error::EBADFLAGS)
244 );
245 assert_eq!(
246 Error::try_from(c_ares_sys::ares_status_t::ARES_ENONAME),
247 Ok(Error::ENONAME)
248 );
249 assert_eq!(
250 Error::try_from(c_ares_sys::ares_status_t::ARES_EBADHINTS),
251 Ok(Error::EBADHINTS)
252 );
253 assert_eq!(
254 Error::try_from(c_ares_sys::ares_status_t::ARES_ENOTINITIALIZED),
255 Ok(Error::ENOTINITIALIZED)
256 );
257 assert_eq!(
258 Error::try_from(c_ares_sys::ares_status_t::ARES_ELOADIPHLPAPI),
259 Ok(Error::ELOADIPHLPAPI)
260 );
261 assert_eq!(
262 Error::try_from(c_ares_sys::ares_status_t::ARES_EADDRGETNETWORKPARAMS),
263 Ok(Error::EADDRGETNETWORKPARAMS)
264 );
265 assert_eq!(
266 Error::try_from(c_ares_sys::ares_status_t::ARES_ECANCELLED),
267 Ok(Error::ECANCELLED)
268 );
269 assert_eq!(
270 Error::try_from(c_ares_sys::ares_status_t::ARES_ESERVICE),
271 Ok(Error::ESERVICE)
272 );
273 assert_eq!(
274 Error::try_from(c_ares_sys::ares_status_t::ARES_ENOSERVER),
275 Ok(Error::ENOSERVER)
276 );
277 }
278
279 #[test]
280 fn is_std_error() {
281 fn assert_std_error<T: std::error::Error>() {}
282 assert_std_error::<Error>();
283 }
284
285 #[test]
286 fn display_all_variants() {
287 let errors = [
288 Error::ENODATA,
289 Error::EFORMERR,
290 Error::ESERVFAIL,
291 Error::ENOTFOUND,
292 Error::ENOTIMP,
293 Error::EREFUSED,
294 Error::EBADQUERY,
295 Error::EBADNAME,
296 Error::EBADFAMILY,
297 Error::EBADRESP,
298 Error::ECONNREFUSED,
299 Error::ETIMEOUT,
300 Error::EOF,
301 Error::EFILE,
302 Error::ENOMEM,
303 Error::EDESTRUCTION,
304 Error::EBADSTR,
305 Error::EBADFLAGS,
306 Error::ENONAME,
307 Error::EBADHINTS,
308 Error::ENOTINITIALIZED,
309 Error::ELOADIPHLPAPI,
310 Error::EADDRGETNETWORKPARAMS,
311 Error::ECANCELLED,
312 Error::ESERVICE,
313 Error::ENOSERVER,
314 Error::UNKNOWN,
315 ];
316 for error in &errors {
317 let display = format!("{error}");
318 assert!(!display.is_empty(), "Error {error:?} has empty display");
319 }
320 }
321
322 #[test]
323 fn debug_format() {
324 let error = Error::ETIMEOUT;
325 let debug = format!("{error:?}");
326 assert!(debug.contains("ETIMEOUT"));
327 }
328
329 #[test]
330 #[allow(clippy::clone_on_copy)]
331 fn clone_and_copy() {
332 let error = Error::EBADNAME;
333 let cloned = error.clone();
334 let copied = error;
335 assert_eq!(error, cloned);
336 assert_eq!(error, copied);
337 }
338
339 #[test]
340 fn eq_and_hash() {
341 let mut set = HashSet::new();
342 set.insert(Error::ENODATA);
343 set.insert(Error::ENOTFOUND);
344 set.insert(Error::ENODATA);
345 assert_eq!(set.len(), 2);
346 }
347
348 #[test]
349 fn ord() {
350 assert!(Error::ENODATA < Error::UNKNOWN);
351 }
352
353 #[test]
354 fn is_send() {
355 fn assert_send<T: Send>() {}
356 assert_send::<Error>();
357 }
358
359 #[test]
360 fn is_sync() {
361 fn assert_sync<T: Sync>() {}
362 assert_sync::<Error>();
363 }
364}