use core::fmt;
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct VmemError {
code: Option<u32>,
invalid_arg: bool,
}
impl VmemError {
#[must_use]
#[inline]
pub const fn invalid_argument() -> Self {
Self {
code: None,
invalid_arg: true,
}
}
#[must_use]
#[inline]
pub const fn from_os_code(code: u32) -> Self {
Self {
code: Some(code),
invalid_arg: false,
}
}
#[must_use]
#[inline]
pub const fn os_refusal_unknown_code() -> Self {
Self {
code: None,
invalid_arg: false,
}
}
#[must_use]
#[inline]
pub const fn os_code(&self) -> Option<u32> {
self.code
}
#[must_use]
#[inline]
pub const fn is_invalid_argument(&self) -> bool {
self.invalid_arg
}
#[must_use]
pub fn last_os_error() -> Self {
match last_os_error_code() {
Some(code) => Self::from_os_code(code),
None => Self::os_refusal_unknown_code(),
}
}
}
impl fmt::Debug for VmemError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.invalid_arg {
f.write_str("VmemError::InvalidArgument")
} else {
f.debug_struct("VmemError")
.field("os_code", &self.code)
.finish()
}
}
}
impl fmt::Display for VmemError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.invalid_arg {
f.write_str("invalid argument (argument contract violation)")
} else {
match self.code {
Some(code) => write!(f, "OS virtual-memory error (code {code})"),
None => f.write_str(
"OS virtual-memory error (unknown OS error code — either a \
genuine OS refusal with an unreadable cause, or the crate \
rejected an unusable OS grant, e.g. a granted address-zero \
mapping)",
),
}
}
}
}
impl std::error::Error for VmemError {}
impl From<VmemError> for std::io::Error {
fn from(e: VmemError) -> Self {
match e.os_code() {
Some(code) => {
match i32::try_from(code) {
Ok(signed) => std::io::Error::from_raw_os_error(signed),
Err(_) => {
std::io::Error::other(e)
}
}
}
None if e.is_invalid_argument() => {
std::io::Error::new(std::io::ErrorKind::InvalidInput, e)
}
None => std::io::Error::other(e),
}
}
}
#[cfg(not(miri))]
fn last_os_error_code() -> Option<u32> {
std::io::Error::last_os_error()
.raw_os_error()
.map(|c| c as u32)
}
#[cfg(miri)]
fn last_os_error_code() -> Option<u32> {
None
}