Skip to main content

assay_workflow/
types.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3use std::str::FromStr;
4use utoipa::ToSchema;
5
6// ── Workflow Status ─────────────────────────────────────────
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
9#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
10pub enum WorkflowStatus {
11    Pending,
12    Running,
13    Waiting,
14    Completed,
15    Failed,
16    Cancelled,
17    TimedOut,
18}
19
20impl fmt::Display for WorkflowStatus {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        match self {
23            Self::Pending => write!(f, "PENDING"),
24            Self::Running => write!(f, "RUNNING"),
25            Self::Waiting => write!(f, "WAITING"),
26            Self::Completed => write!(f, "COMPLETED"),
27            Self::Failed => write!(f, "FAILED"),
28            Self::Cancelled => write!(f, "CANCELLED"),
29            Self::TimedOut => write!(f, "TIMED_OUT"),
30        }
31    }
32}
33
34impl FromStr for WorkflowStatus {
35    type Err = String;
36
37    fn from_str(s: &str) -> Result<Self, Self::Err> {
38        match s {
39            "PENDING" => Ok(Self::Pending),
40            "RUNNING" => Ok(Self::Running),
41            "WAITING" => Ok(Self::Waiting),
42            "COMPLETED" => Ok(Self::Completed),
43            "FAILED" => Ok(Self::Failed),
44            "CANCELLED" => Ok(Self::Cancelled),
45            "TIMED_OUT" => Ok(Self::TimedOut),
46            _ => Err(format!("unknown workflow status: {s}")),
47        }
48    }
49}
50
51impl WorkflowStatus {
52    pub fn is_terminal(self) -> bool {
53        matches!(
54            self,
55            Self::Completed | Self::Failed | Self::Cancelled | Self::TimedOut
56        )
57    }
58}
59
60// ── Activity Status ─────────────────────────────────────────
61
62#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
63#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
64pub enum ActivityStatus {
65    Pending,
66    Running,
67    Completed,
68    Failed,
69    Cancelled,
70}
71
72impl fmt::Display for ActivityStatus {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        match self {
75            Self::Pending => write!(f, "PENDING"),
76            Self::Running => write!(f, "RUNNING"),
77            Self::Completed => write!(f, "COMPLETED"),
78            Self::Failed => write!(f, "FAILED"),
79            Self::Cancelled => write!(f, "CANCELLED"),
80        }
81    }
82}
83
84impl FromStr for ActivityStatus {
85    type Err = String;
86
87    fn from_str(s: &str) -> Result<Self, Self::Err> {
88        match s {
89            "PENDING" => Ok(Self::Pending),
90            "RUNNING" => Ok(Self::Running),
91            "COMPLETED" => Ok(Self::Completed),
92            "FAILED" => Ok(Self::Failed),
93            "CANCELLED" => Ok(Self::Cancelled),
94            _ => Err(format!("unknown activity status: {s}")),
95        }
96    }
97}
98
99// ── Event Types ─────────────────────────────────────────────
100
101#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
102pub enum EventType {
103    WorkflowStarted,
104    ActivityScheduled,
105    ActivityCompleted,
106    ActivityFailed,
107    TimerStarted,
108    TimerFired,
109    SignalReceived,
110    WorkflowCompleted,
111    WorkflowFailed,
112    WorkflowCancelled,
113    ChildWorkflowStarted,
114    ChildWorkflowCompleted,
115    SideEffectRecorded,
116}
117
118impl fmt::Display for EventType {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        let s = serde_json::to_value(self)
121            .ok()
122            .and_then(|v| v.as_str().map(String::from))
123            .unwrap_or_else(|| format!("{self:?}"));
124        write!(f, "{s}")
125    }
126}
127
128impl FromStr for EventType {
129    type Err = String;
130
131    fn from_str(s: &str) -> Result<Self, Self::Err> {
132        serde_json::from_value(serde_json::Value::String(s.to_string()))
133            .map_err(|_| format!("unknown event type: {s}"))
134    }
135}
136
137// ── Overlap Policy ──────────────────────────────────────────
138
139#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
140#[serde(rename_all = "snake_case")]
141pub enum OverlapPolicy {
142    Skip,
143    Queue,
144    CancelOld,
145    AllowAll,
146}
147
148impl fmt::Display for OverlapPolicy {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        match self {
151            Self::Skip => write!(f, "skip"),
152            Self::Queue => write!(f, "queue"),
153            Self::CancelOld => write!(f, "cancel_old"),
154            Self::AllowAll => write!(f, "allow_all"),
155        }
156    }
157}
158
159impl FromStr for OverlapPolicy {
160    type Err = String;
161
162    fn from_str(s: &str) -> Result<Self, Self::Err> {
163        match s {
164            "skip" => Ok(Self::Skip),
165            "queue" => Ok(Self::Queue),
166            "cancel_old" => Ok(Self::CancelOld),
167            "allow_all" => Ok(Self::AllowAll),
168            _ => Err(format!("unknown overlap policy: {s}")),
169        }
170    }
171}
172
173// ── Records ─────────────────────────────────────────────────
174
175#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
176pub struct WorkflowRecord {
177    pub id: String,
178    pub namespace: String,
179    pub run_id: String,
180    pub workflow_type: String,
181    pub task_queue: String,
182    pub status: String,
183    pub input: Option<String>,
184    pub result: Option<String>,
185    pub error: Option<String>,
186    pub parent_id: Option<String>,
187    pub claimed_by: Option<String>,
188    /// Application-level indexed metadata, JSON object encoded as a string
189    /// (e.g. `{"env":"prod","tenant":"acme","progress":0.5}`). Settable on
190    /// workflow start and updatable at runtime via
191    /// `ctx:upsert_search_attributes(...)` from workflow code. Filter the
192    /// list endpoint with `?search_attrs={"key":"value"}`.
193    pub search_attributes: Option<String>,
194    /// Set when the archival task has moved this workflow's
195    /// events+activities+snapshots off to cold storage. The row itself
196    /// stays (with `archive_uri` pointing at the bundle) so that
197    /// `GET /workflows/{id}` still resolves.
198    pub archived_at: Option<f64>,
199    pub archive_uri: Option<String>,
200    pub created_at: f64,
201    pub updated_at: f64,
202    pub completed_at: Option<f64>,
203}
204
205#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
206pub struct WorkflowEvent {
207    pub id: Option<i64>,
208    pub workflow_id: String,
209    pub seq: i32,
210    pub event_type: String,
211    pub payload: Option<String>,
212    pub timestamp: f64,
213}
214
215/// Options for scheduling an activity. All fields default to sensible values
216/// when not provided by the caller; this keeps the per-call API short while
217/// still letting workflows tune retry/timeout policy when they need to.
218#[derive(Clone, Debug, Default, Serialize, Deserialize, ToSchema)]
219pub struct ScheduleActivityOpts {
220    pub max_attempts: Option<i32>,
221    pub initial_interval_secs: Option<f64>,
222    pub backoff_coefficient: Option<f64>,
223    pub start_to_close_secs: Option<f64>,
224    pub heartbeat_timeout_secs: Option<f64>,
225}
226
227#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
228pub struct WorkflowActivity {
229    pub id: Option<i64>,
230    pub workflow_id: String,
231    pub seq: i32,
232    pub name: String,
233    pub task_queue: String,
234    pub input: Option<String>,
235    pub status: String,
236    pub result: Option<String>,
237    pub error: Option<String>,
238    pub attempt: i32,
239    pub max_attempts: i32,
240    pub initial_interval_secs: f64,
241    pub backoff_coefficient: f64,
242    pub start_to_close_secs: f64,
243    pub heartbeat_timeout_secs: Option<f64>,
244    pub claimed_by: Option<String>,
245    pub scheduled_at: f64,
246    pub started_at: Option<f64>,
247    pub completed_at: Option<f64>,
248    pub last_heartbeat: Option<f64>,
249}
250
251#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
252pub struct WorkflowTimer {
253    pub id: Option<i64>,
254    pub workflow_id: String,
255    pub seq: i32,
256    pub fire_at: f64,
257    pub fired: bool,
258}
259
260#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
261pub struct WorkflowSignal {
262    pub id: Option<i64>,
263    pub workflow_id: String,
264    pub name: String,
265    pub payload: Option<String>,
266    pub consumed: bool,
267    pub received_at: f64,
268}
269
270#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
271pub struct WorkflowSchedule {
272    pub name: String,
273    pub namespace: String,
274    pub workflow_type: String,
275    pub cron_expr: String,
276    /// IANA time-zone name used to interpret `cron_expr` (e.g. "Europe/Berlin",
277    /// "America/New_York"). Defaults to "UTC" when a schedule is created
278    /// without an explicit timezone, preserving v0.11.2 behaviour.
279    pub timezone: String,
280    pub input: Option<String>,
281    pub task_queue: String,
282    pub overlap_policy: String,
283    pub paused: bool,
284    pub last_run_at: Option<f64>,
285    pub next_run_at: Option<f64>,
286    pub last_workflow_id: Option<String>,
287    pub created_at: f64,
288}
289
290/// Partial update to a `WorkflowSchedule`. Only fields set to `Some` are
291/// applied; `None` leaves the existing value untouched. Used by
292/// `PATCH /api/v1/schedules/{name}`.
293#[derive(Clone, Debug, Default, Serialize, Deserialize, ToSchema)]
294pub struct SchedulePatch {
295    pub cron_expr: Option<String>,
296    pub timezone: Option<String>,
297    pub input: Option<serde_json::Value>,
298    pub task_queue: Option<String>,
299    pub overlap_policy: Option<String>,
300}
301
302#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
303pub struct WorkflowWorker {
304    pub id: String,
305    pub namespace: String,
306    pub identity: String,
307    pub task_queue: String,
308    pub workflows: Option<String>,
309    pub activities: Option<String>,
310    pub max_concurrent_workflows: i32,
311    pub max_concurrent_activities: i32,
312    pub active_tasks: i32,
313    pub last_heartbeat: f64,
314    pub registered_at: f64,
315}
316
317#[derive(Clone, Debug, Serialize, Deserialize)]
318pub struct WorkflowSnapshot {
319    pub workflow_id: String,
320    pub event_seq: i32,
321    pub state_json: String,
322    pub created_at: f64,
323}