use core::fmt;
use std::{
str::FromStr,
sync::{
Arc,
atomic::{AtomicU8, Ordering},
},
};
#[repr(u8)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum Status {
#[default]
Pending,
Queued,
Running,
Done,
Failed,
Killed,
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum StatusError {
#[error("Unknown state: {0}")]
UnknownState(String),
}
impl FromStr for Status {
type Err = StatusError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Pending" => Ok(Self::Pending),
"Queued" => Ok(Self::Queued),
"Running" => Ok(Self::Running),
"Done" => Ok(Self::Done),
"Failed" => Ok(Self::Failed),
"Killed" => Ok(Self::Killed),
_ => Err(StatusError::UnknownState(s.to_owned())),
}
}
}
impl fmt::Display for Status {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
Self::Pending => write!(f, "Pending"),
Self::Queued => write!(f, "Queued"),
Self::Running => write!(f, "Running"),
Self::Done => write!(f, "Done"),
Self::Failed => write!(f, "Failed"),
Self::Killed => write!(f, "Killed"),
}
}
}
impl Status {
fn from_u8(val: u8) -> Option<Self> {
match val {
0 => Some(Self::Pending),
1 => Some(Self::Queued),
2 => Some(Self::Running),
3 => Some(Self::Done),
4 => Some(Self::Failed),
5 => Some(Self::Killed),
_ => None,
}
}
}
#[repr(transparent)]
#[derive(Debug, Clone, Default)]
pub struct AtomicStatus(Arc<AtomicU8>);
impl AtomicStatus {
#[must_use]
pub fn new(status: Status) -> Self {
Self(Arc::new(AtomicU8::new(status as u8)))
}
#[must_use]
pub fn load(&self) -> Status {
Status::from_u8(self.0.load(Ordering::SeqCst)).unwrap()
}
pub fn store(&self, status: Status) {
self.0.store(status as u8, Ordering::SeqCst);
}
#[must_use]
pub fn swap(&self, status: Status) -> Status {
Status::from_u8(self.0.swap(status as u8, Ordering::AcqRel)).unwrap()
}
}
impl From<AtomicStatus> for Status {
fn from(val: AtomicStatus) -> Self {
val.load()
}
}
impl From<Status> for AtomicStatus {
fn from(val: Status) -> Self {
Self::new(val)
}
}
#[cfg(feature = "serde")]
mod serde_impl {
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::*;
fn serialize<S>(status: &AtomicStatus, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let value = status.load();
serializer.serialize_str(&value.to_string())
}
fn deserialize<'de, D>(deserializer: D) -> Result<AtomicStatus, D::Error>
where
D: Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;
let status = Status::from_str(&value).map_err(serde::de::Error::custom)?;
Ok(AtomicStatus::new(status))
}
impl Serialize for AtomicStatus {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serialize(self, serializer)
}
}
impl<'de> Deserialize<'de> for AtomicStatus {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserialize(deserializer)
}
}
}