use std::collections::HashMap;
use std::sync::Arc;
use serde_json::Value;
use tokio::sync::Mutex;
#[derive(Clone, Copy)]
#[allow(dead_code)]
pub enum TaskKind {
Image,
Video,
}
impl TaskKind {
fn as_str(self) -> &'static str {
match self {
TaskKind::Image => "image",
TaskKind::Video => "video",
}
}
}
enum Status {
Pending,
Completed(Value),
Failed(String),
}
struct TaskEntry {
kind: TaskKind,
status: Status,
}
pub struct TaskSnapshot {
pub kind: &'static str,
pub status: &'static str,
pub result: Option<Value>,
pub error: Option<String>,
}
#[derive(Clone, Default)]
pub struct TaskRegistry {
inner: Arc<Mutex<HashMap<String, TaskEntry>>>,
}
impl TaskRegistry {
pub fn new() -> Self {
Self::default()
}
pub async fn insert_pending(&self, id: &str, kind: TaskKind) {
self.inner.lock().await.insert(
id.to_string(),
TaskEntry {
kind,
status: Status::Pending,
},
);
}
pub async fn complete(&self, id: &str, result: Value) {
if let Some(entry) = self.inner.lock().await.get_mut(id) {
entry.status = Status::Completed(result);
}
}
pub async fn fail(&self, id: &str, error: String) {
if let Some(entry) = self.inner.lock().await.get_mut(id) {
entry.status = Status::Failed(error);
}
}
pub async fn snapshot(&self, id: &str) -> Option<TaskSnapshot> {
let guard = self.inner.lock().await;
let entry = guard.get(id)?;
Some(match &entry.status {
Status::Pending => TaskSnapshot {
kind: entry.kind.as_str(),
status: "pending",
result: None,
error: None,
},
Status::Completed(v) => TaskSnapshot {
kind: entry.kind.as_str(),
status: "completed",
result: Some(v.clone()),
error: None,
},
Status::Failed(err) => TaskSnapshot {
kind: entry.kind.as_str(),
status: "failed",
result: None,
error: Some(err.clone()),
},
})
}
}
#[cfg(test)]
mod tests {
use serde_json::json;
use super::*;
#[tokio::test]
async fn lifecycle_pending_completed_failed_and_unknown() {
let reg = TaskRegistry::new();
assert!(reg.snapshot("missing").await.is_none());
reg.insert_pending("a", TaskKind::Image).await;
let s = reg.snapshot("a").await.unwrap();
assert_eq!(s.status, "pending");
assert_eq!(s.kind, "image");
reg.complete("a", json!({"ok": true, "n": 1})).await;
let s = reg.snapshot("a").await.unwrap();
assert_eq!(s.status, "completed");
assert_eq!(s.result.unwrap()["n"], 1);
reg.insert_pending("b", TaskKind::Video).await;
reg.fail("b", "boom".to_string()).await;
let s = reg.snapshot("b").await.unwrap();
assert_eq!(s.status, "failed");
assert_eq!(s.kind, "video");
assert_eq!(s.error.as_deref(), Some("boom"));
}
}