use alloc::string::{String, ToString};
pub type StatusCode = u32;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Status {
Success, NoStat, NoPool, BadDate, InvalidSocket, NoProcess, NoTime, NoDirectory, NoLock, NoPoll, NoSocket, NoThread, NoThreadKey, NoSharedMemoryAvailable, DSOOpen, General, BadIpAddress, BadMask, SymbolNotFound, NotEnoughEntropy,
InChild, InParent, Detach, NotDetach, ChildDone, ChildNotDone, TimeUp, Incomplete, BadCh, BadArgument, Eof, NotFound, Anonymous, FileBased, KeyBased, Initializer, NotImplemented, Mismatch, Absolute, Relative, IncompleteError, AboveRoot, Busy, ProcessUnknown, }
impl Status {
pub fn is_success(&self) -> bool {
matches!(self, Status::Success)
}
pub fn is_error(&self) -> bool {
!self.is_success()
}
pub fn raw_os_error(&self) -> Option<i32> {
let status_code: u32 = (*self).into();
match self {
Status::Success => None,
_ if status_code >= apr_sys::APR_OS_START_SYSERR => {
Some((status_code - apr_sys::APR_OS_START_SYSERR) as i32)
}
_ => None,
}
}
pub fn strerror(&self) -> String {
let status_code: u32 = (*self).into();
let buf = unsafe {
let mut buf = [0u8; 1024];
apr_sys::apr_strerror(
status_code as apr_sys::apr_status_t,
buf.as_mut_ptr() as *mut core::ffi::c_char,
buf.len(),
);
buf
};
String::from_utf8_lossy(&buf).to_string()
}
}
impl From<u32> for Status {
fn from(status: u32) -> Self {
match status {
apr_sys::APR_SUCCESS => Status::Success,
apr_sys::APR_ENOSTAT => Status::NoStat,
apr_sys::APR_ENOPOOL => Status::NoPool,
apr_sys::APR_EBADDATE => Status::BadDate,
apr_sys::APR_EINVALSOCK => Status::InvalidSocket,
apr_sys::APR_ENOPROC => Status::NoProcess,
apr_sys::APR_ENOTIME => Status::NoTime,
apr_sys::APR_ENODIR => Status::NoDirectory,
apr_sys::APR_ENOLOCK => Status::NoLock,
apr_sys::APR_ENOPOLL => Status::NoPoll,
apr_sys::APR_ENOSOCKET => Status::NoSocket,
apr_sys::APR_ENOTHREAD => Status::NoThread,
apr_sys::APR_ENOTHDKEY => Status::NoThreadKey,
apr_sys::APR_ENOSHMAVAIL => Status::NoSharedMemoryAvailable,
apr_sys::APR_EDSOOPEN => Status::DSOOpen,
apr_sys::APR_EGENERAL => Status::General,
apr_sys::APR_EBADIP => Status::BadIpAddress,
apr_sys::APR_EBADMASK => Status::BadMask,
apr_sys::APR_ESYMNOTFOUND => Status::SymbolNotFound,
apr_sys::APR_ENOTENOUGHENTROPY => Status::NotEnoughEntropy,
apr_sys::APR_INCHILD => Status::InChild,
apr_sys::APR_INPARENT => Status::InParent,
apr_sys::APR_DETACH => Status::Detach,
apr_sys::APR_NOTDETACH => Status::NotDetach,
apr_sys::APR_CHILD_DONE => Status::ChildDone,
apr_sys::APR_CHILD_NOTDONE => Status::ChildNotDone,
apr_sys::APR_TIMEUP => Status::TimeUp,
apr_sys::APR_INCOMPLETE => Status::Incomplete,
apr_sys::APR_BADCH => Status::BadCh,
apr_sys::APR_BADARG => Status::BadArgument,
apr_sys::APR_EOF => Status::Eof,
apr_sys::APR_NOTFOUND => Status::NotFound,
apr_sys::APR_ANONYMOUS => Status::Anonymous,
apr_sys::APR_FILEBASED => Status::FileBased,
apr_sys::APR_KEYBASED => Status::KeyBased,
apr_sys::APR_EINIT => Status::Initializer,
apr_sys::APR_ENOTIMPL => Status::NotImplemented,
apr_sys::APR_EMISMATCH => Status::Mismatch,
apr_sys::APR_EABSOLUTE => Status::Absolute,
apr_sys::APR_ERELATIVE => Status::Relative,
apr_sys::APR_EINCOMPLETE => Status::IncompleteError,
apr_sys::APR_EABOVEROOT => Status::AboveRoot,
apr_sys::APR_EBUSY => Status::Busy,
apr_sys::APR_EPROC_UNKNOWN => Status::ProcessUnknown,
_ => Status::General,
}
}
}
impl core::fmt::Display for Status {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let status_code: u32 = (*self).into();
write!(f, "{} ({})", self.strerror(), status_code)
}
}
#[cfg(feature = "std")]
impl std::error::Error for Status {}
impl From<Status> for u32 {
fn from(status: Status) -> Self {
match status {
Status::Success => apr_sys::APR_SUCCESS,
Status::NoStat => apr_sys::APR_ENOSTAT,
Status::NoPool => apr_sys::APR_ENOPOOL,
Status::BadDate => apr_sys::APR_EBADDATE,
Status::InvalidSocket => apr_sys::APR_EINVALSOCK,
Status::NoProcess => apr_sys::APR_ENOPROC,
Status::NoTime => apr_sys::APR_ENOTIME,
Status::NoDirectory => apr_sys::APR_ENODIR,
Status::NoLock => apr_sys::APR_ENOLOCK,
Status::NoPoll => apr_sys::APR_ENOPOLL,
Status::NoSocket => apr_sys::APR_ENOSOCKET,
Status::NoThread => apr_sys::APR_ENOTHREAD,
Status::NoThreadKey => apr_sys::APR_ENOTHDKEY,
Status::NoSharedMemoryAvailable => apr_sys::APR_ENOSHMAVAIL,
Status::DSOOpen => apr_sys::APR_EDSOOPEN,
Status::General => apr_sys::APR_EGENERAL,
Status::BadIpAddress => apr_sys::APR_EBADIP,
Status::BadMask => apr_sys::APR_EBADMASK,
Status::SymbolNotFound => apr_sys::APR_ESYMNOTFOUND,
Status::NotEnoughEntropy => apr_sys::APR_ENOTENOUGHENTROPY,
Status::InChild => apr_sys::APR_INCHILD,
Status::InParent => apr_sys::APR_INPARENT,
Status::Detach => apr_sys::APR_DETACH,
Status::NotDetach => apr_sys::APR_NOTDETACH,
Status::ChildDone => apr_sys::APR_CHILD_DONE,
Status::ChildNotDone => apr_sys::APR_CHILD_NOTDONE,
Status::TimeUp => apr_sys::APR_TIMEUP,
Status::Incomplete => apr_sys::APR_INCOMPLETE,
Status::BadCh => apr_sys::APR_BADCH,
Status::BadArgument => apr_sys::APR_BADARG,
Status::Eof => apr_sys::APR_EOF,
Status::NotFound => apr_sys::APR_NOTFOUND,
Status::Anonymous => apr_sys::APR_ANONYMOUS,
Status::FileBased => apr_sys::APR_FILEBASED,
Status::KeyBased => apr_sys::APR_KEYBASED,
Status::Initializer => apr_sys::APR_EINIT,
Status::NotImplemented => apr_sys::APR_ENOTIMPL,
Status::Mismatch => apr_sys::APR_EMISMATCH,
Status::Absolute => apr_sys::APR_EABSOLUTE,
Status::Relative => apr_sys::APR_ERELATIVE,
Status::IncompleteError => apr_sys::APR_EINCOMPLETE,
Status::AboveRoot => apr_sys::APR_EABOVEROOT,
Status::Busy => apr_sys::APR_EBUSY,
Status::ProcessUnknown => apr_sys::APR_EPROC_UNKNOWN,
}
}
}
impl From<i32> for Status {
fn from(status: i32) -> Self {
(status as u32).into()
}
}
#[cfg(feature = "std")]
impl From<std::io::ErrorKind> for Status {
fn from(kind: std::io::ErrorKind) -> Self {
(kind as u32).into()
}
}
#[cfg(feature = "std")]
impl From<std::io::Error> for Status {
fn from(error: std::io::Error) -> Self {
error.kind().into()
}
}
#[cfg(feature = "std")]
impl From<Status> for std::io::Error {
fn from(status: Status) -> Self {
let kind = match status {
Status::NotFound | Status::NoDirectory => std::io::ErrorKind::NotFound,
Status::BadArgument | Status::InvalidSocket => std::io::ErrorKind::InvalidInput,
Status::Eof => std::io::ErrorKind::UnexpectedEof,
Status::Busy => std::io::ErrorKind::ResourceBusy,
Status::TimeUp => std::io::ErrorKind::TimedOut,
_ => return std::io::Error::other(status),
};
std::io::Error::new(kind, status)
}
}
pub fn apr_result(status_code: i32) -> Result<(), Status> {
if status_code == apr_sys::APR_SUCCESS as i32 {
Ok(())
} else {
Err(Status::from(status_code))
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::format;
#[test]
fn test_status_to_u32_returns_apr_value() {
let status = Status::Success;
let code: u32 = status.into();
assert_eq!(code, apr_sys::APR_SUCCESS);
let status = Status::NotFound;
let code: u32 = status.into();
assert_eq!(code, apr_sys::APR_NOTFOUND);
let status = Status::Eof;
let code: u32 = status.into();
assert_eq!(code, apr_sys::APR_EOF);
}
#[test]
fn test_status_roundtrip() {
let original = apr_sys::APR_ENOSTAT;
let status = Status::from(original);
let code: u32 = status.into();
assert_eq!(code, original);
let original = apr_sys::APR_BADARG;
let status = Status::from(original);
let code: u32 = status.into();
assert_eq!(code, original);
}
#[test]
fn test_status_display_uses_correct_value() {
let status = Status::NotFound;
let display = format!("{}", status);
assert!(display.contains(&format!("({})", apr_sys::APR_NOTFOUND)));
}
}