diff options
Diffstat (limited to 'src/lib.rs')
-rw-r--r-- | src/lib.rs | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..20875b5 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,156 @@ +//! Cross-platform interface to the `errno` variable. +//! +//! # Examples +//! ``` +//! use errno::{Errno, errno, set_errno}; +//! +//! // Get the current value of errno +//! let e = errno(); +//! +//! // Set the current value of errno +//! set_errno(e); +//! +//! // Extract the error code as an i32 +//! let code = e.0; +//! +//! // Display a human-friendly error message +//! println!("Error {}: {}", code, e); +//! ``` + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg_attr(unix, path = "unix.rs")] +#[cfg_attr(windows, path = "windows.rs")] +#[cfg_attr(target_os = "wasi", path = "wasi.rs")] +#[cfg_attr(target_os = "hermit", path = "hermit.rs")] +mod sys; + +use core::fmt; +#[cfg(feature = "std")] +use std::error::Error; +#[cfg(feature = "std")] +use std::io; + +/// Wraps a platform-specific error code. +/// +/// The `Display` instance maps the code to a human-readable string. It +/// calls [`strerror_r`][1] under POSIX, and [`FormatMessageW`][2] on +/// Windows. +/// +/// [1]: http://pubs.opengroup.org/onlinepubs/009695399/functions/strerror.html +/// [2]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351%28v=vs.85%29.aspx +#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd, Hash)] +pub struct Errno(pub i32); + +impl fmt::Debug for Errno { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + sys::with_description(*self, |desc| { + fmt.debug_struct("Errno") + .field("code", &self.0) + .field("description", &desc.ok()) + .finish() + }) + } +} + +impl fmt::Display for Errno { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + sys::with_description(*self, |desc| match desc { + Ok(desc) => fmt.write_str(desc), + Err(fm_err) => write!( + fmt, + "OS error {} ({} returned error {})", + self.0, + sys::STRERROR_NAME, + fm_err.0 + ), + }) + } +} + +impl From<Errno> for i32 { + fn from(e: Errno) -> Self { + e.0 + } +} + +#[cfg(feature = "std")] +impl Error for Errno { + // TODO: Remove when MSRV >= 1.27 + #[allow(deprecated)] + fn description(&self) -> &str { + "system error" + } +} + +#[cfg(feature = "std")] +impl From<Errno> for io::Error { + fn from(errno: Errno) -> Self { + io::Error::from_raw_os_error(errno.0) + } +} + +/// Returns the platform-specific value of `errno`. +pub fn errno() -> Errno { + sys::errno() +} + +/// Sets the platform-specific value of `errno`. +pub fn set_errno(err: Errno) { + sys::set_errno(err) +} + +#[test] +fn it_works() { + let x = errno(); + set_errno(x); +} + +#[cfg(feature = "std")] +#[test] +fn it_works_with_to_string() { + let x = errno(); + let _ = x.to_string(); +} + +#[cfg(feature = "std")] +#[test] +fn check_description() { + let expect = if cfg!(windows) { + "Incorrect function." + } else if cfg!(target_os = "illumos") { + "Not owner" + } else if cfg!(target_os = "wasi") { + "Argument list too long" + } else if cfg!(target_os = "haiku") { + "Operation not allowed" + } else { + "Operation not permitted" + }; + + let errno_code = if cfg!(target_os = "haiku") { + -2147483633 + } else { + 1 + }; + set_errno(Errno(errno_code)); + + assert_eq!(errno().to_string(), expect); + assert_eq!( + format!("{:?}", errno()), + format!( + "Errno {{ code: {}, description: Some({:?}) }}", + errno_code, expect + ) + ); +} + +#[cfg(feature = "std")] +#[test] +fn check_error_into_errno() { + const ERROR_CODE: i32 = 1; + + let error = io::Error::from_raw_os_error(ERROR_CODE); + let new_error: io::Error = Errno(ERROR_CODE).into(); + assert_eq!(error.kind(), new_error.kind()); +} |