Skip to main content

c_ares/
error.rs

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        /// Error codes that the library might return.
11        #[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 error.
18            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    /// DNS server returned answer with no data.
47    ENODATA => ARES_ENODATA,
48    /// DNS server claims query was misformatted.
49    EFORMERR => ARES_EFORMERR,
50    /// DNS server returned general failure.
51    ESERVFAIL => ARES_ESERVFAIL,
52    /// Domain name not found.
53    ENOTFOUND => ARES_ENOTFOUND,
54    /// DNS server does not implement requested operation.
55    ENOTIMP => ARES_ENOTIMP,
56    /// DNS server refused query.
57    EREFUSED => ARES_EREFUSED,
58    /// Misformatted DNS query.
59    EBADQUERY => ARES_EBADQUERY,
60    /// Misformatted domain name.
61    EBADNAME => ARES_EBADNAME,
62    /// Unsupported address family.
63    EBADFAMILY => ARES_EBADFAMILY,
64    /// Misformatted DNS reply.
65    EBADRESP => ARES_EBADRESP,
66    /// Could not contact DNS servers.
67    ECONNREFUSED => ARES_ECONNREFUSED,
68    /// Timeout while contacting DNS servers.
69    ETIMEOUT => ARES_ETIMEOUT,
70    /// End of file.
71    EOF => ARES_EOF,
72    /// Error reading file.
73    EFILE => ARES_EFILE,
74    /// Out of memory.
75    ENOMEM => ARES_ENOMEM,
76    /// Channel is being destroyed.
77    EDESTRUCTION => ARES_EDESTRUCTION,
78    /// Misformatted string.
79    EBADSTR => ARES_EBADSTR,
80    /// Illegal flags specified.
81    EBADFLAGS => ARES_EBADFLAGS,
82    /// Given hostname is not numeric.
83    ENONAME => ARES_ENONAME,
84    /// Illegal hints flags specified.
85    EBADHINTS => ARES_EBADHINTS,
86    /// c-ares library initialization not yet performed.
87    ENOTINITIALIZED => ARES_ENOTINITIALIZED,
88    /// Error loading iphlpapi.dll.
89    ELOADIPHLPAPI => ARES_ELOADIPHLPAPI,
90    /// Could not find GetNetworkParams function.
91    EADDRGETNETWORKPARAMS => ARES_EADDRGETNETWORKPARAMS,
92    /// DNS query cancelled.
93    ECANCELLED => ARES_ECANCELLED,
94    /// The textual service name provided could not be dereferenced into a port.
95    ESERVICE => ARES_ESERVICE,
96    /// No DNS servers were configured.
97    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
112/// The type used by this library for methods that might fail.
113pub 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)] // exhaustive table-driven test
171    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}