chatora-errno 1.0.2

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

const TMP_BUF_SZ: usize = 1024;

extern "C" {
    #[thread_local]
    #[link_name = "errno"]
    static mut libc_errno: libc::c_int;

    fn strerror_r(errnum: libc::c_int, buf: *mut libc::c_char, buflen: libc::size_t) -> 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 { libc_errno } }

#[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 {
        libc_errno = 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)
            );
        })
    }
}