1use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6pub use crate::storage::tasks::{TaskKind, TaskStatus};
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
10#[serde(transparent)]
11pub struct TaskId(pub String);
12
13impl TaskId {
14 pub fn new() -> Self {
15 Self(Uuid::now_v7().to_string())
16 }
17
18 pub fn parse(s: &str) -> Result<Self, uuid::Error> {
19 Uuid::parse_str(s)?;
20 Ok(Self(s.to_string()))
21 }
22
23 pub fn as_str(&self) -> &str {
24 &self.0
25 }
26}
27
28impl std::fmt::Display for TaskId {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 f.write_str(&self.0)
31 }
32}
33
34impl Default for TaskId {
35 fn default() -> Self {
36 Self::new()
37 }
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
42#[serde(rename_all = "snake_case")]
43pub enum CoreEvent {
44 TaskStarted,
45 TaskCompleted,
46 TaskFailed,
47 TaskCancelled,
48}
49
50impl CoreEvent {
51 pub fn as_str(self) -> &'static str {
52 match self {
53 Self::TaskStarted => "task_started",
54 Self::TaskCompleted => "task_completed",
55 Self::TaskFailed => "task_failed",
56 Self::TaskCancelled => "task_cancelled",
57 }
58 }
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
63pub struct BatchFetchParams {
64 pub urls: Vec<String>,
65 #[serde(default = "default_concurrency")]
66 pub concurrency: u32,
67 #[serde(default = "default_per_domain")]
68 pub per_domain_concurrency: u32,
69 #[serde(default)]
70 pub force_refresh: bool,
71}
72
73fn default_concurrency() -> u32 {
74 8
75}
76fn default_per_domain() -> u32 {
77 2
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
82pub struct RetryParams {
83 pub url: String,
84 pub attempt: u8,
85 pub wait_ms_initial: u64,
86 pub max_attempts: u8,
87 #[serde(default)]
88 pub parent_task_id: Option<String>,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
93pub struct RevalidateParams {
94 pub url: String,
95 #[serde(default)]
96 pub etag_at_serve: Option<String>,
97 #[serde(default)]
98 pub last_modified_at_serve: Option<String>,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
103pub struct BatchFetchResult {
104 pub total: u32,
105 pub succeeded: u32,
106 pub failed: u32,
107 pub duration_ms: i64,
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn task_id_is_uuid_v7_string() {
116 let id = TaskId::new();
117 let parsed = Uuid::parse_str(id.as_str()).unwrap();
118 assert_eq!(parsed.get_version_num(), 7);
119 }
120
121 #[test]
122 fn task_id_parse_roundtrip() {
123 let id = TaskId::new();
124 let again = TaskId::parse(id.as_str()).unwrap();
125 assert_eq!(id, again);
126 }
127
128 #[test]
129 fn task_id_parse_rejects_garbage() {
130 assert!(TaskId::parse("not-a-uuid").is_err());
131 }
132
133 #[test]
134 fn batch_fetch_params_defaults() {
135 let v: BatchFetchParams = serde_json::from_str(r#"{"urls":["a"]}"#).unwrap();
136 assert_eq!(v.concurrency, 8);
137 assert_eq!(v.per_domain_concurrency, 2);
138 assert!(!v.force_refresh);
139 }
140
141 #[test]
142 fn core_event_as_str_table() {
143 assert_eq!(CoreEvent::TaskStarted.as_str(), "task_started");
144 assert_eq!(CoreEvent::TaskFailed.as_str(), "task_failed");
145 }
146}