use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::agent::AgentId;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TaskId(Uuid);
impl TaskId {
#[must_use]
pub fn new() -> Self {
Self(Uuid::new_v4())
}
#[must_use]
pub fn from_uuid(uuid: Uuid) -> Self {
Self(uuid)
}
#[must_use]
pub fn short_id(&self) -> String {
self.0.to_string().chars().take(4).collect()
}
}
impl Default for TaskId {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Display for TaskId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::str::FromStr for TaskId {
type Err = uuid::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(Uuid::parse_str(s)?))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum TaskState {
#[default]
Pending,
Assigned,
#[serde(alias = "in_flight")]
Running,
Completed,
Failed,
Cancelled,
}
impl TaskState {
#[must_use]
pub fn is_terminal(&self) -> bool {
matches!(self, Self::Completed | Self::Failed | Self::Cancelled)
}
#[must_use]
pub fn is_in_flight(&self) -> bool {
matches!(self, Self::Running)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Task {
pub id: TaskId,
pub description: String,
pub state: TaskState,
pub assigned_to: Option<AgentId>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
#[serde(default)]
pub started_at: Option<DateTime<Utc>>,
pub completed_at: Option<DateTime<Utc>>,
pub result: Option<String>,
pub parent_id: Option<TaskId>,
#[serde(default)]
pub tags: Vec<String>,
}
impl Task {
#[must_use]
pub fn new(description: impl Into<String>) -> Self {
let now = Utc::now();
Self {
id: TaskId::new(),
description: description.into(),
state: TaskState::Pending,
assigned_to: None,
created_at: now,
updated_at: now,
started_at: None,
completed_at: None,
result: None,
parent_id: None,
tags: Vec::new(),
}
}
#[must_use]
pub fn with_tags(mut self, tags: impl IntoIterator<Item = impl Into<String>>) -> Self {
self.tags = tags.into_iter().map(Into::into).collect();
self
}
#[must_use]
pub fn with_parent(mut self, parent: TaskId) -> Self {
self.parent_id = Some(parent);
self
}
pub fn assign(&mut self, agent: AgentId) {
self.assigned_to = Some(agent);
self.state = TaskState::Assigned;
self.updated_at = Utc::now();
}
pub fn start(&mut self) {
let now = Utc::now();
self.state = TaskState::Running;
self.started_at = Some(now);
self.updated_at = now;
}
pub fn complete(&mut self, result: impl Into<String>) {
self.state = TaskState::Completed;
self.result = Some(result.into());
self.completed_at = Some(Utc::now());
self.updated_at = Utc::now();
}
pub fn fail(&mut self, error: impl Into<String>) {
self.state = TaskState::Failed;
self.result = Some(error.into());
self.completed_at = Some(Utc::now());
self.updated_at = Utc::now();
}
}