use std::{
fmt,
sync::atomic::{AtomicU64, Ordering},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct TaskId(u64);
impl TaskId {
#[must_use]
pub fn new() -> Self {
static COUNTER: AtomicU64 = AtomicU64::new(1);
Self(COUNTER.fetch_add(1, Ordering::Relaxed))
}
#[inline]
#[must_use]
pub const fn as_u64(self) -> u64 {
self.0
}
#[inline]
#[must_use]
pub const fn from_raw(value: u64) -> Self {
Self(value)
}
}
impl Default for TaskId {
fn default() -> Self {
Self::new()
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
impl fmt::Display for TaskId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Task({})", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Priority(pub u32);
impl Priority {
pub const CRITICAL: Self = Self(0);
pub const HIGH: Self = Self(50);
pub const NORMAL: Self = Self(100);
pub const LOW: Self = Self(200);
pub const IDLE: Self = Self(1000);
#[inline]
#[must_use]
pub const fn new(value: u32) -> Self {
Self(value)
}
#[inline]
#[must_use]
pub const fn as_u32(self) -> u32 {
self.0
}
}
impl Default for Priority {
fn default() -> Self {
Self::NORMAL
}
}
impl fmt::Display for Priority {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::CRITICAL => write!(f, "Critical(0)"),
Self::HIGH => write!(f, "High(50)"),
Self::NORMAL => write!(f, "Normal(100)"),
Self::LOW => write!(f, "Low(200)"),
Self::IDLE => write!(f, "Idle(1000)"),
Self(v) => write!(f, "Priority({v})"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum TaskState {
#[default]
Pending,
Running,
Completed,
Failed,
}
impl TaskState {
#[inline]
#[must_use]
pub const fn is_pending(&self) -> bool {
matches!(self, Self::Pending)
}
#[inline]
#[must_use]
pub const fn is_finished(&self) -> bool {
matches!(self, Self::Completed | Self::Failed)
}
}
impl fmt::Display for TaskState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Pending => write!(f, "Pending"),
Self::Running => write!(f, "Running"),
Self::Completed => write!(f, "Completed"),
Self::Failed => write!(f, "Failed"),
}
}
}
pub type BoxedTask = Box<dyn FnOnce() + Send + 'static>;
pub struct Task {
id: TaskId,
priority: Priority,
state: TaskState,
work: Option<BoxedTask>,
name: Option<&'static str>,
}
impl Task {
#[must_use]
pub fn new<F>(work: F) -> Self
where
F: FnOnce() + Send + 'static,
{
Self::with_priority(Priority::NORMAL, work)
}
#[must_use]
pub fn with_priority<F>(priority: Priority, work: F) -> Self
where
F: FnOnce() + Send + 'static,
{
Self {
id: TaskId::new(),
priority,
state: TaskState::Pending,
work: Some(Box::new(work)),
name: None,
}
}
#[must_use]
pub const fn with_name(mut self, name: &'static str) -> Self {
self.name = Some(name);
self
}
#[inline]
#[must_use]
pub const fn id(&self) -> TaskId {
self.id
}
#[inline]
#[must_use]
pub const fn priority(&self) -> Priority {
self.priority
}
#[inline]
#[must_use]
pub const fn state(&self) -> TaskState {
self.state
}
#[inline]
#[must_use]
pub const fn name(&self) -> Option<&'static str> {
self.name
}
#[inline]
#[must_use]
#[cfg_attr(coverage_nightly, coverage(off))]
pub const fn is_executable(&self) -> bool {
matches!(self.state, TaskState::Pending) && self.work.is_some()
}
pub fn execute(&mut self) -> Result<(), &'static str> {
if self.state != TaskState::Pending {
return Err("task already executed or cancelled");
}
let work = self.work.take().ok_or("work closure missing")?;
self.state = TaskState::Running;
work();
self.state = TaskState::Completed;
Ok(())
}
pub fn mark_failed(&mut self) {
self.work = None;
self.state = TaskState::Failed;
}
pub fn cancel(&mut self) {
self.work = None;
self.state = TaskState::Failed;
}
}
impl fmt::Debug for Task {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Task")
.field("id", &self.id)
.field("priority", &self.priority)
.field("state", &self.state)
.field("name", &self.name)
.field("has_work", &self.work.is_some())
.finish()
}
}