Skip to main content

apalis_core/task/
status.rs

1//! The status of a task
2//!
3//! ## Overview
4//!
5//! The `Status` enum defines the various states
6//! a task can be in, such as `Pending`, `Running`, `Done`, `Failed`, etc.
7//!
8//! - It includes functionality for parsing a `Status` from a string and formatting it for display.
9//! - This is useful for tracking the lifecycle of tasks.
10use core::fmt;
11use std::{
12    str::FromStr,
13    sync::{
14        Arc,
15        atomic::{AtomicU8, Ordering},
16    },
17};
18
19/// Represents the state of a task
20#[repr(u8)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22#[non_exhaustive]
23#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
24pub enum Status {
25    /// Task is pending
26    #[default]
27    Pending,
28    /// Task is queued for execution, but no worker has picked it up
29    Queued,
30    /// Task is running
31    Running,
32    /// Task was done successfully
33    Done,
34    /// Task has failed.
35    Failed,
36    /// Task has been killed
37    Killed,
38}
39
40/// Errors that can occur when parsing a `Status` from a string
41#[derive(Debug, thiserror::Error)]
42#[non_exhaustive]
43pub enum StatusError {
44    #[error("Unknown state: {0}")]
45    /// Unknown state error
46    UnknownState(String),
47}
48
49impl FromStr for Status {
50    type Err = StatusError;
51
52    fn from_str(s: &str) -> Result<Self, Self::Err> {
53        match s {
54            "Pending" => Ok(Self::Pending),
55            "Queued" => Ok(Self::Queued),
56            "Running" => Ok(Self::Running),
57            "Done" => Ok(Self::Done),
58            "Failed" => Ok(Self::Failed),
59            "Killed" => Ok(Self::Killed),
60            _ => Err(StatusError::UnknownState(s.to_owned())),
61        }
62    }
63}
64
65impl fmt::Display for Status {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        match &self {
68            Self::Pending => write!(f, "Pending"),
69            Self::Queued => write!(f, "Queued"),
70            Self::Running => write!(f, "Running"),
71            Self::Done => write!(f, "Done"),
72            Self::Failed => write!(f, "Failed"),
73            Self::Killed => write!(f, "Killed"),
74        }
75    }
76}
77
78impl Status {
79    fn from_u8(val: u8) -> Option<Self> {
80        match val {
81            0 => Some(Self::Pending),
82            1 => Some(Self::Queued),
83            2 => Some(Self::Running),
84            3 => Some(Self::Done),
85            4 => Some(Self::Failed),
86            5 => Some(Self::Killed),
87            _ => None,
88        }
89    }
90}
91
92/// Atomic version of `Status` for concurrent scenarios
93#[repr(transparent)]
94#[derive(Debug, Clone, Default)]
95pub struct AtomicStatus(Arc<AtomicU8>);
96
97impl AtomicStatus {
98    /// Create a new `AtomicStatus` with the given initial status
99    #[must_use]
100    pub fn new(status: Status) -> Self {
101        Self(Arc::new(AtomicU8::new(status as u8)))
102    }
103
104    /// Load the current status
105    #[must_use]
106    pub fn load(&self) -> Status {
107        Status::from_u8(self.0.load(Ordering::SeqCst)).unwrap()
108    }
109
110    /// Store a new status
111    pub fn store(&self, status: Status) {
112        self.0.store(status as u8, Ordering::SeqCst);
113    }
114    /// Swap the current status with a new one, returning the old status
115    #[must_use]
116    pub fn swap(&self, status: Status) -> Status {
117        Status::from_u8(self.0.swap(status as u8, Ordering::AcqRel)).unwrap()
118    }
119}
120
121impl From<AtomicStatus> for Status {
122    fn from(val: AtomicStatus) -> Self {
123        val.load()
124    }
125}
126
127impl From<Status> for AtomicStatus {
128    fn from(val: Status) -> Self {
129        Self::new(val)
130    }
131}
132
133#[cfg(feature = "serde")]
134mod serde_impl {
135    use serde::{Deserialize, Deserializer, Serialize, Serializer};
136
137    use super::*;
138
139    // Custom serialization function
140    fn serialize<S>(status: &AtomicStatus, serializer: S) -> Result<S::Ok, S::Error>
141    where
142        S: Serializer,
143    {
144        let value = status.load();
145        serializer.serialize_str(&value.to_string())
146    }
147
148    // Custom deserialization function
149    fn deserialize<'de, D>(deserializer: D) -> Result<AtomicStatus, D::Error>
150    where
151        D: Deserializer<'de>,
152    {
153        let value = String::deserialize(deserializer)?;
154        let status = Status::from_str(&value).map_err(serde::de::Error::custom)?;
155        Ok(AtomicStatus::new(status))
156    }
157
158    impl Serialize for AtomicStatus {
159        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
160        where
161            S: Serializer,
162        {
163            serialize(self, serializer)
164        }
165    }
166
167    impl<'de> Deserialize<'de> for AtomicStatus {
168        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
169        where
170            D: Deserializer<'de>,
171        {
172            deserialize(deserializer)
173        }
174    }
175}