use core::fmt;
use crate::fourcc::FourCharCode;
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct OsStatus(pub i32);
impl OsStatus {
pub const OK: Self = Self(0);
pub const NOT_RUNNING: Self = Self::from_fourcc(FourCharCode::new(*b"stop"));
pub const UNSPECIFIED: Self = Self::from_fourcc(FourCharCode::new(*b"what"));
pub const UNKNOWN_PROPERTY: Self = Self::from_fourcc(FourCharCode::new(*b"who?"));
pub const BAD_PROPERTY_SIZE: Self = Self::from_fourcc(FourCharCode::new(*b"!siz"));
pub const ILLEGAL_OPERATION: Self = Self::from_fourcc(FourCharCode::new(*b"nope"));
pub const BAD_OBJECT: Self = Self::from_fourcc(FourCharCode::new(*b"!obj"));
pub const BAD_DEVICE: Self = Self::from_fourcc(FourCharCode::new(*b"!dev"));
pub const BAD_STREAM: Self = Self::from_fourcc(FourCharCode::new(*b"!str"));
pub const UNSUPPORTED_OPERATION: Self = Self::from_fourcc(FourCharCode::new(*b"unop"));
pub const NOT_READY: Self = Self::from_fourcc(FourCharCode::new(*b"nrdy"));
pub const UNSUPPORTED_FORMAT: Self = Self::from_fourcc(FourCharCode::new(*b"!dat"));
pub const PERMISSIONS: Self = Self::from_fourcc(FourCharCode::new(*b"!hog"));
#[inline]
#[must_use]
pub const fn from_fourcc(code: FourCharCode) -> Self {
Self(code.as_u32() as i32)
}
#[inline]
#[must_use]
pub const fn as_i32(self) -> i32 {
self.0
}
#[inline]
#[must_use]
pub const fn is_ok(self) -> bool {
self.0 == 0
}
#[inline]
#[must_use]
pub const fn is_err(self) -> bool {
self.0 != 0
}
#[inline]
pub const fn ok(self) -> Result<(), Self> {
if self.is_ok() {
Ok(())
} else {
Err(self)
}
}
}
impl fmt::Debug for OsStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_ok() {
return write!(f, "OsStatus::OK");
}
let code = FourCharCode::from_u32(self.0 as u32);
if code.is_printable() {
write!(f, "OsStatus({code})")
} else {
write!(f, "OsStatus({} / 0x{:08X})", self.0, self.0 as u32)
}
}
}
impl fmt::Display for OsStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl From<i32> for OsStatus {
#[inline]
fn from(value: i32) -> Self {
Self(value)
}
}
impl From<OsStatus> for i32 {
#[inline]
fn from(value: OsStatus) -> Self {
value.0
}
}
impl From<Result<(), OsStatus>> for OsStatus {
#[inline]
fn from(value: Result<(), OsStatus>) -> Self {
match value {
Ok(()) => Self::OK,
Err(status) => status,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ok_classifies_correctly() {
assert!(OsStatus::OK.is_ok());
assert!(!OsStatus::OK.is_err());
assert_eq!(OsStatus::OK.as_i32(), 0);
}
#[test]
fn error_codes_classify_correctly() {
for e in [
OsStatus::UNSPECIFIED,
OsStatus::UNKNOWN_PROPERTY,
OsStatus::BAD_OBJECT,
OsStatus::BAD_DEVICE,
OsStatus::BAD_STREAM,
OsStatus::ILLEGAL_OPERATION,
OsStatus::UNSUPPORTED_FORMAT,
] {
assert!(e.is_err(), "{e:?} should be an error");
assert!(!e.is_ok());
}
}
#[test]
fn ok_method_converts_to_result() {
assert_eq!(OsStatus::OK.ok(), Ok(()));
assert_eq!(OsStatus::BAD_OBJECT.ok(), Err(OsStatus::BAD_OBJECT));
}
#[test]
fn result_flattens_back_to_status() {
assert_eq!(OsStatus::from(Ok(())), OsStatus::OK);
assert_eq!(
OsStatus::from(Err(OsStatus::NOT_READY)),
OsStatus::NOT_READY
);
}
#[test]
fn error_constants_match_fourcc_values() {
assert_eq!(OsStatus::BAD_OBJECT.as_i32(), 0x216F_626A); assert_eq!(OsStatus::UNKNOWN_PROPERTY.as_i32(), 0x77686F3F); assert_eq!(OsStatus::NOT_RUNNING.as_i32(), 0x73746F70); }
#[test]
fn round_trips_through_i32() {
for e in [OsStatus::OK, OsStatus::BAD_DEVICE, OsStatus::PERMISSIONS] {
let raw: i32 = e.into();
assert_eq!(OsStatus::from(raw), e);
}
}
#[test]
fn debug_renders_ok_specially() {
assert_eq!(format!("{:?}", OsStatus::OK), "OsStatus::OK");
}
#[test]
fn debug_renders_fourcc_errors() {
assert_eq!(format!("{:?}", OsStatus::BAD_OBJECT), "OsStatus('!obj')");
assert_eq!(
format!("{:?}", OsStatus::UNKNOWN_PROPERTY),
"OsStatus('who?')"
);
}
#[test]
fn debug_renders_non_fourcc_errors_numerically() {
let s = OsStatus(-1);
assert_eq!(format!("{s:?}"), "OsStatus(-1 / 0xFFFFFFFF)");
}
#[test]
fn layout_is_transparent_i32() {
use core::mem::{align_of, size_of};
assert_eq!(size_of::<OsStatus>(), size_of::<i32>());
assert_eq!(align_of::<OsStatus>(), align_of::<i32>());
}
}