chatora-errno 1.0.2

A thin Rust wrapper library around libc errno interface
Documentation
//! Implementation of [`errno`][errno] functionality for Unix systems.
//!
//! [errno]: https://en.wikipedia.org/wiki/Errno.h

const TMP_BUF_SZ: usize = 128;

extern "C" {
    #[cfg_attr(
        any(
            target_os = "linux",
            target_os = "emscripten",
            target_os = "fuchsia",
            target_os = "l4re"
        ),
        link_name = "__errno_location"
    )]
    #[cfg_attr(
        any(
            target_os = "netbsd",
            target_os = "openbsd",
            target_os = "android",
            target_os = "redox",
            target_env = "newlib"
        ),
        link_name = "__errno"
    )]
    #[cfg_attr(
        any(target_os = "solaris", target_os = "illumos"),
        link_name = "___errno"
    )]
    #[cfg_attr(
        any(target_os = "macos", target_os = "ios", target_os = "freebsd"),
        link_name = "__error"
    )]
    #[cfg_attr(target_os = "haiku", link_name = "_errnop")]
    fn errno_location() -> *mut libc::c_int;

    #[cfg_attr(
        any(target_os = "linux", target_env = "newlib"),
        link_name = "__xpg_strerror_r"
    )]
    fn strerror_r(errnum: libc::c_int, buf: *mut libc::c_char, buflen: libc::size_t)
        -> libc::c_int;
}

#[allow(private_doc_tests)]
/// Returns the value of errno.
///
/// # Examples
/// ```
/// # use chatora_errno::get_errno;
/// let errno = get_errno();
/// assert_eq!(errno, 0);
/// ```
#[inline]
pub fn get_errno() -> i32 { unsafe { *errno_location() } }

#[allow(private_doc_tests)]
/// Sets the value of errno.
///
/// # Examples
/// ```
/// # use chatora_errno::{get_errno, set_errno};
/// set_errno(0);
/// assert_eq!(get_errno(), 0);
/// ```
#[inline]
pub fn set_errno(errno: i32) { unsafe { *errno_location() = errno } }

#[allow(private_doc_tests)]
/// Sets `0` value of errno. Equivalent to [`set_errno`](fn@set_errno)`(0)`
///
/// # Examples
/// ```
/// # use chatora_errno::{get_errno, clear_errno};
/// clear_errno();
/// assert_eq!(get_errno(), 0);
/// ```
#[inline]
pub fn clear_errno() { set_errno(0); }

#[allow(private_doc_tests)]
/// Gets a detailed `String` description for the given errno number.
///
/// # Panics
/// This function panics if the bytes passed to [`libc::c_char`](libc::c_char)
/// buffer are not valid UTF-8.
///
/// # Examples
/// ```
/// # use chatora_errno::{get_errno, describe_errno};
/// let errno = get_errno();
/// let err_string: String = describe_errno(errno).unwrap();
/// assert_eq!(err_string, "Success");
/// ```
#[inline]
pub fn describe_errno(errno: i32) -> std::io::Result<String> {
    let mut buf: [libc::c_char; TMP_BUF_SZ] = [0; TMP_BUF_SZ];

    unsafe {
        if strerror_r(errno, buf.as_mut_ptr(), buf.len()) < 0 {
            let e = get_errno();
            if e != libc::ERANGE {
                return Err(std::io::Error::from_raw_os_error(e));
            }
        }
        Ok(
            std::str::from_utf8(std::ffi::CStr::from_ptr(buf.as_ptr()).to_bytes())
                .unwrap()
                .to_owned(),
        )
    }
}

#[cfg(test)]
mod test {
    use super::*;

    // Test get_errno(), set_errno() and clear_errno()
    #[test]
    fn set_get_errno() {
        (-1..134).for_each(|err_code| {
            set_errno(err_code);
            assert_eq!(err_code, get_errno());
        });
        clear_errno();
    }

    // Test describe_errno()
    #[test]
    fn error_string() {
        (1..134).for_each(|err_code| {
            let err_string = describe_errno(err_code).unwrap();
            let os_err = std::io::Error::from_raw_os_error(err_code);
            assert_eq!(
                format!("{} (os error {})", err_string, err_code),
                format!("{}", os_err)
            );
        })
    }
}