use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DtStatus(pub u32);
impl DtStatus {
pub const DT_FAILURE: u32 = 1u32 << 31;
pub const DT_SUCCESS: u32 = 1u32 << 30;
pub const DT_IN_PROGRESS: u32 = 1u32 << 29;
pub const DT_STATUS_DETAIL_MASK: u32 = 0x0ffffff;
pub const DT_WRONG_MAGIC: u32 = 1 << 0;
pub const DT_WRONG_VERSION: u32 = 1 << 1;
pub const DT_OUT_OF_MEMORY: u32 = 1 << 2;
pub const DT_INVALID_PARAM: u32 = 1 << 3;
pub const DT_BUFFER_TOO_SMALL: u32 = 1 << 4;
pub const DT_OUT_OF_NODES: u32 = 1 << 5;
pub const DT_PARTIAL_RESULT: u32 = 1 << 6;
pub const DT_ALREADY_OCCUPIED: u32 = 1 << 7;
pub const fn new(flags: u32) -> Self {
Self(flags)
}
pub const fn success() -> Self {
Self(Self::DT_SUCCESS)
}
pub const fn failure() -> Self {
Self(Self::DT_FAILURE)
}
pub const fn failure_detail(detail: u32) -> Self {
Self(Self::DT_FAILURE | detail)
}
pub const fn success_detail(detail: u32) -> Self {
Self(Self::DT_SUCCESS | detail)
}
pub const fn in_progress() -> Self {
Self(Self::DT_IN_PROGRESS)
}
pub fn is_success(&self) -> bool {
(self.0 & Self::DT_SUCCESS) != 0
}
pub fn is_failure(&self) -> bool {
(self.0 & Self::DT_FAILURE) != 0
}
pub fn is_in_progress(&self) -> bool {
(self.0 & Self::DT_IN_PROGRESS) != 0
}
pub fn has_detail(&self, detail: u32) -> bool {
(self.0 & detail) != 0
}
pub fn detail(&self) -> u32 {
self.0 & Self::DT_STATUS_DETAIL_MASK
}
}
impl fmt::Display for DtStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_success() {
write!(f, "Success")?;
} else if self.is_failure() {
write!(f, "Failure")?;
} else if self.is_in_progress() {
write!(f, "In Progress")?;
}
let mut details = Vec::new();
if self.has_detail(Self::DT_WRONG_MAGIC) {
details.push("Wrong Magic");
}
if self.has_detail(Self::DT_WRONG_VERSION) {
details.push("Wrong Version");
}
if self.has_detail(Self::DT_OUT_OF_MEMORY) {
details.push("Out of Memory");
}
if self.has_detail(Self::DT_INVALID_PARAM) {
details.push("Invalid Param");
}
if self.has_detail(Self::DT_BUFFER_TOO_SMALL) {
details.push("Buffer Too Small");
}
if self.has_detail(Self::DT_OUT_OF_NODES) {
details.push("Out of Nodes");
}
if self.has_detail(Self::DT_PARTIAL_RESULT) {
details.push("Partial Result");
}
if self.has_detail(Self::DT_ALREADY_OCCUPIED) {
details.push("Already Occupied");
}
if !details.is_empty() {
write!(f, " ({})", details.join(", "))?;
}
Ok(())
}
}
impl Default for DtStatus {
fn default() -> Self {
Self::success()
}
}
impl std::error::Error for DtStatus {}
pub fn dt_status_succeed(status: DtStatus) -> bool {
status.is_success()
}
pub fn dt_status_failed(status: DtStatus) -> bool {
status.is_failure()
}
pub fn dt_status_in_progress(status: DtStatus) -> bool {
status.is_in_progress()
}
pub fn dt_status_detail(status: DtStatus, detail: u32) -> bool {
status.has_detail(detail)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_status_flags() {
let success = DtStatus::success();
assert!(success.is_success());
assert!(!success.is_failure());
let failure = DtStatus::failure();
assert!(!failure.is_success());
assert!(failure.is_failure());
let in_progress = DtStatus::in_progress();
assert!(in_progress.is_in_progress());
assert!(!in_progress.is_success());
assert!(!in_progress.is_failure());
}
#[test]
fn test_status_with_detail() {
let failure_oom = DtStatus::failure_detail(DtStatus::DT_OUT_OF_MEMORY);
assert!(failure_oom.is_failure());
assert!(failure_oom.has_detail(DtStatus::DT_OUT_OF_MEMORY));
assert!(!failure_oom.has_detail(DtStatus::DT_INVALID_PARAM));
let success_partial = DtStatus::success_detail(DtStatus::DT_PARTIAL_RESULT);
assert!(success_partial.is_success());
assert!(success_partial.has_detail(DtStatus::DT_PARTIAL_RESULT));
}
#[test]
fn test_multiple_details() {
let status = DtStatus::new(
DtStatus::DT_FAILURE | DtStatus::DT_INVALID_PARAM | DtStatus::DT_BUFFER_TOO_SMALL,
);
assert!(status.is_failure());
assert!(status.has_detail(DtStatus::DT_INVALID_PARAM));
assert!(status.has_detail(DtStatus::DT_BUFFER_TOO_SMALL));
assert!(!status.has_detail(DtStatus::DT_OUT_OF_MEMORY));
}
}