chatora_errno/
unix.rs

1//! Implementation of [`errno`][errno] functionality for Unix systems.
2//!
3//! [errno]: https://en.wikipedia.org/wiki/Errno.h
4
5const TMP_BUF_SZ: usize = 128;
6
7extern "C" {
8    #[cfg_attr(
9        any(
10            target_os = "linux",
11            target_os = "emscripten",
12            target_os = "fuchsia",
13            target_os = "l4re"
14        ),
15        link_name = "__errno_location"
16    )]
17    #[cfg_attr(
18        any(
19            target_os = "netbsd",
20            target_os = "openbsd",
21            target_os = "android",
22            target_os = "redox",
23            target_env = "newlib"
24        ),
25        link_name = "__errno"
26    )]
27    #[cfg_attr(
28        any(target_os = "solaris", target_os = "illumos"),
29        link_name = "___errno"
30    )]
31    #[cfg_attr(
32        any(target_os = "macos", target_os = "ios", target_os = "freebsd"),
33        link_name = "__error"
34    )]
35    #[cfg_attr(target_os = "haiku", link_name = "_errnop")]
36    fn errno_location() -> *mut libc::c_int;
37
38    #[cfg_attr(
39        any(target_os = "linux", target_env = "newlib"),
40        link_name = "__xpg_strerror_r"
41    )]
42    fn strerror_r(errnum: libc::c_int, buf: *mut libc::c_char, buflen: libc::size_t)
43        -> libc::c_int;
44}
45
46#[allow(private_doc_tests)]
47/// Returns the value of errno.
48///
49/// # Examples
50/// ```
51/// # use chatora_errno::get_errno;
52/// let errno = get_errno();
53/// assert_eq!(errno, 0);
54/// ```
55#[inline]
56pub fn get_errno() -> i32 { unsafe { *errno_location() } }
57
58#[allow(private_doc_tests)]
59/// Sets the value of errno.
60///
61/// # Examples
62/// ```
63/// # use chatora_errno::{get_errno, set_errno};
64/// set_errno(0);
65/// assert_eq!(get_errno(), 0);
66/// ```
67#[inline]
68pub fn set_errno(errno: i32) { unsafe { *errno_location() = errno } }
69
70#[allow(private_doc_tests)]
71/// Sets `0` value of errno. Equivalent to [`set_errno`](fn@set_errno)`(0)`
72///
73/// # Examples
74/// ```
75/// # use chatora_errno::{get_errno, clear_errno};
76/// clear_errno();
77/// assert_eq!(get_errno(), 0);
78/// ```
79#[inline]
80pub fn clear_errno() { set_errno(0); }
81
82#[allow(private_doc_tests)]
83/// Gets a detailed `String` description for the given errno number.
84///
85/// # Panics
86/// This function panics if the bytes passed to [`libc::c_char`](libc::c_char)
87/// buffer are not valid UTF-8.
88///
89/// # Examples
90/// ```
91/// # use chatora_errno::{get_errno, describe_errno};
92/// let errno = get_errno();
93/// let err_string: String = describe_errno(errno).unwrap();
94/// assert_eq!(err_string, "Success");
95/// ```
96#[inline]
97pub fn describe_errno(errno: i32) -> std::io::Result<String> {
98    let mut buf: [libc::c_char; TMP_BUF_SZ] = [0; TMP_BUF_SZ];
99
100    unsafe {
101        if strerror_r(errno, buf.as_mut_ptr(), buf.len()) < 0 {
102            let e = get_errno();
103            if e != libc::ERANGE {
104                return Err(std::io::Error::from_raw_os_error(e));
105            }
106        }
107        Ok(
108            std::str::from_utf8(std::ffi::CStr::from_ptr(buf.as_ptr()).to_bytes())
109                .unwrap()
110                .to_owned(),
111        )
112    }
113}
114
115#[cfg(test)]
116mod test {
117    use super::*;
118
119    // Test get_errno(), set_errno() and clear_errno()
120    #[test]
121    fn set_get_errno() {
122        (-1..134).for_each(|err_code| {
123            set_errno(err_code);
124            assert_eq!(err_code, get_errno());
125        });
126        clear_errno();
127    }
128
129    // Test describe_errno()
130    #[test]
131    fn error_string() {
132        (1..134).for_each(|err_code| {
133            let err_string = describe_errno(err_code).unwrap();
134            let os_err = std::io::Error::from_raw_os_error(err_code);
135            assert_eq!(
136                format!("{} (os error {})", err_string, err_code),
137                format!("{}", os_err)
138            );
139        })
140    }
141}