Skip to content

Commit d03c1ed

Browse files
Copilotbcho
andauthored
Export TaskState and RunState enums from extension module (#34)
* Initial plan * Add TaskState and RunState enums to extension module Co-authored-by: bcho <1975118+bcho@users.noreply.github.com> * Format code with cargo fmt Co-authored-by: bcho <1975118+bcho@users.noreply.github.com> * Use TaskState and RunState enums in extension code Replace string literals with typed enums throughout the codebase: - checkpoint.rs: Parse task state and use TaskState::Cancelled - event.rs: Parse task/run states and use TaskState::Cancelled, RunState::Running - run.rs: Use TaskState and RunState enums for state management and comparisons - claim.rs: Use TaskState and RunState enums for state management State values are read from database as strings, parsed into enums for type-safe comparisons, and converted back to strings when writing to database. Co-authored-by: bcho <1975118+bcho@users.noreply.github.com> * Make match patterns exhaustive for run state mapping Replace catch-all patterns with explicit exhaustive matches that use unreachable!() for impossible states. This makes the code more explicit about which states can occur and improves compile-time checking. Co-authored-by: bcho <1975118+bcho@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: bcho <1975118+bcho@users.noreply.github.com>
1 parent 1b1840f commit d03c1ed

6 files changed

Lines changed: 256 additions & 21 deletions

File tree

‎absurd-sqlite-extension/src/checkpoint.rs‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::sql;
2+
use crate::types::TaskState;
23
use crate::validate;
34
use serde_json::Value as JsonValue;
45
use sqlite3ext_sys::sqlite3;
@@ -93,8 +94,11 @@ pub fn absurd_set_task_checkpoint_state(
9394
let task_state = row
9495
.get::<String>(1)
9596
.map_err(|err| Error::new_message(format!("failed to read task state: {:?}", err)))?;
97+
let task_state = task_state
98+
.parse::<TaskState>()
99+
.map_err(|err| Error::new_message(format!("invalid task state: {}", err)))?;
96100

97-
if task_state == "cancelled" {
101+
if task_state == TaskState::Cancelled {
98102
return Err(Error::new_message("Task has been cancelled"));
99103
}
100104

‎absurd-sqlite-extension/src/claim.rs‎

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::retry;
22
use crate::sql;
3+
use crate::types::{RunState, TaskState};
34
use crate::validate;
45
use serde_json::Value as JsonValue;
56
use sqlite3ext_sys::sqlite3;
@@ -241,7 +242,7 @@ fn expire_claims(db: *mut sqlite3, queue_name: &str, now: i64) -> Result<()> {
241242
Some(max_attempts)
242243
};
243244
let allow_retry = max_attempts_opt.is_none_or(|max| next_attempt <= max);
244-
let mut task_state = "failed";
245+
let mut task_state = TaskState::Failed;
245246
let mut last_attempt_run = run_id.clone();
246247
let mut cancelled_at = "";
247248
let mut recorded_attempt = attempt;
@@ -261,17 +262,18 @@ fn expire_claims(db: *mut sqlite3, queue_name: &str, now: i64) -> Result<()> {
261262
};
262263

263264
if cancel_task {
264-
task_state = "cancelled";
265+
task_state = TaskState::Cancelled;
265266
cancelled_at = &now_value;
266267
} else {
267268
let new_run_id = Uuid::now_v7().to_string();
268269
let next_available_value = next_available.to_string();
269270
let next_attempt_value = next_attempt.to_string();
270271
let run_state = if next_available > now {
271-
"sleeping"
272+
RunState::Sleeping
272273
} else {
273-
"pending"
274+
RunState::Pending
274275
};
276+
let run_state_str = run_state.to_string();
275277
sql::exec_with_bind_text(
276278
db,
277279
"insert into absurd_runs (
@@ -303,17 +305,29 @@ fn expire_claims(db: *mut sqlite3, queue_name: &str, now: i64) -> Result<()> {
303305
&new_run_id,
304306
&task_id,
305307
&next_attempt_value,
306-
run_state,
308+
&run_state_str,
307309
&next_available_value,
308310
],
309311
)?;
310-
task_state = run_state;
312+
task_state = match run_state {
313+
RunState::Sleeping => TaskState::Sleeping,
314+
RunState::Pending => TaskState::Pending,
315+
// These states are impossible here since run_state is derived from
316+
// the conditional above which only produces Sleeping or Pending
317+
RunState::Running
318+
| RunState::Completed
319+
| RunState::Failed
320+
| RunState::Cancelled => {
321+
unreachable!("run_state can only be Sleeping or Pending in this context")
322+
}
323+
};
311324
last_attempt_run = new_run_id;
312325
recorded_attempt = next_attempt;
313326
}
314327
}
315328

316329
let attempt_value = recorded_attempt.to_string();
330+
let task_state_str = task_state.to_string();
317331
sql::exec_with_bind_text(
318332
db,
319333
"update absurd_tasks
@@ -327,7 +341,7 @@ fn expire_claims(db: *mut sqlite3, queue_name: &str, now: i64) -> Result<()> {
327341
where queue_name = ?5
328342
and task_id = ?6",
329343
&[
330-
task_state,
344+
&task_state_str,
331345
&attempt_value,
332346
&last_attempt_run,
333347
cancelled_at,

‎absurd-sqlite-extension/src/event.rs‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::sql;
2+
use crate::types::{RunState, TaskState};
23
use crate::validate;
34
use serde_json::Value as JsonValue;
45
use sqlite3ext_sys::sqlite3;
@@ -146,8 +147,11 @@ fn await_event_impl(
146147
let task_state = run_row
147148
.get::<String>(3)
148149
.map_err(|err| Error::new_message(format!("failed to read task state: {:?}", err)))?;
150+
let task_state = task_state
151+
.parse::<TaskState>()
152+
.map_err(|err| Error::new_message(format!("invalid task state: {}", err)))?;
149153

150-
if task_state == "cancelled" {
154+
if task_state == TaskState::Cancelled {
151155
return Err(Error::new_message("Task has been cancelled"));
152156
}
153157

@@ -191,7 +195,11 @@ fn await_event_impl(
191195
}
192196
}
193197

194-
if run_state != "running" {
198+
let run_state = run_state
199+
.parse::<RunState>()
200+
.map_err(|err| Error::new_message(format!("invalid run state: {}", err)))?;
201+
202+
if run_state != RunState::Running {
195203
return Err(Error::new_message(
196204
"Run must be running to await absurd_events",
197205
));

‎absurd-sqlite-extension/src/lib.rs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@ mod run;
1515
mod settings;
1616
mod spawn;
1717
mod sql;
18+
pub mod types;
1819
mod validate;
1920

21+
// Re-export public types for convenience
22+
pub use types::{RunState, TaskState};
23+
2024
/// SQL: absurd_version()
2125
/// Usage: return extension version and git commit.
2226
/// Section: Meta

‎absurd-sqlite-extension/src/run.rs‎

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::retry;
22
use crate::sql;
3+
use crate::types::{RunState, TaskState};
34
use crate::validate;
45
use chrono::DateTime;
56
use serde_json::Value as JsonValue;
@@ -179,7 +180,7 @@ fn fail_run_impl(
179180
Some(max_attempts)
180181
};
181182
let allow_retry = max_attempts_opt.is_none_or(|max| next_attempt <= max);
182-
let mut task_state = "failed";
183+
let mut task_state = TaskState::Failed;
183184
let mut last_attempt_run = run_id.to_string();
184185
let mut cancelled_at = "";
185186
let mut recorded_attempt = attempt;
@@ -202,17 +203,18 @@ fn fail_run_impl(
202203
};
203204

204205
if cancel_task {
205-
task_state = "cancelled";
206+
task_state = TaskState::Cancelled;
206207
cancelled_at = &now_value;
207208
} else {
208209
let new_run_id = Uuid::now_v7().to_string();
209210
let next_available_value = next_available.to_string();
210211
let next_attempt_value = next_attempt.to_string();
211212
let run_state = if next_available > now {
212-
"sleeping"
213+
RunState::Sleeping
213214
} else {
214-
"pending"
215+
RunState::Pending
215216
};
217+
let run_state_str = run_state.to_string();
216218
sql::exec_with_bind_text(
217219
db,
218220
"insert into absurd_runs (
@@ -244,17 +246,29 @@ fn fail_run_impl(
244246
&new_run_id,
245247
&task_id,
246248
&next_attempt_value,
247-
run_state,
249+
&run_state_str,
248250
&next_available_value,
249251
],
250252
)?;
251-
task_state = run_state;
253+
task_state = match run_state {
254+
RunState::Sleeping => TaskState::Sleeping,
255+
RunState::Pending => TaskState::Pending,
256+
// These states are impossible here since run_state is derived from
257+
// the conditional above which only produces Sleeping or Pending
258+
RunState::Running
259+
| RunState::Completed
260+
| RunState::Failed
261+
| RunState::Cancelled => {
262+
unreachable!("run_state can only be Sleeping or Pending in this context")
263+
}
264+
};
252265
last_attempt_run = new_run_id;
253266
recorded_attempt = next_attempt;
254267
}
255268
}
256269

257270
let attempt_value = recorded_attempt.to_string();
271+
let task_state_str = task_state.to_string();
258272
sql::exec_with_bind_text(
259273
db,
260274
"update absurd_tasks
@@ -268,7 +282,7 @@ fn fail_run_impl(
268282
where queue_name = ?5
269283
and task_id = ?6",
270284
&[
271-
task_state,
285+
&task_state_str,
272286
&attempt_value,
273287
&last_attempt_run,
274288
cancelled_at,
@@ -360,8 +374,11 @@ pub fn absurd_complete_run(
360374
let run_state = row
361375
.get::<String>(1)
362376
.map_err(|err| Error::new_message(format!("failed to read state: {:?}", err)))?;
377+
let run_state = run_state
378+
.parse::<RunState>()
379+
.map_err(|err| Error::new_message(format!("invalid run state: {}", err)))?;
363380

364-
if run_state != "running" {
381+
if run_state != RunState::Running {
365382
return Err(Error::new_message("run is not currently running"));
366383
}
367384

@@ -486,8 +503,11 @@ pub fn absurd_extend_claim(
486503
let task_state = row
487504
.get::<String>(0)
488505
.map_err(|err| Error::new_message(format!("failed to read state: {:?}", err)))?;
506+
let task_state = task_state
507+
.parse::<TaskState>()
508+
.map_err(|err| Error::new_message(format!("invalid task state: {}", err)))?;
489509

490-
if task_state == "cancelled" {
510+
if task_state == TaskState::Cancelled {
491511
return Err(Error::new_message("Task has been cancelled"));
492512
}
493513

@@ -816,8 +836,14 @@ pub fn absurd_cancel_task(
816836
let task_state = row
817837
.get::<String>(0)
818838
.map_err(|err| Error::new_message(format!("failed to read task state: {:?}", err)))?;
819-
820-
if task_state == "completed" || task_state == "failed" || task_state == "cancelled" {
839+
let task_state = task_state
840+
.parse::<TaskState>()
841+
.map_err(|err| Error::new_message(format!("invalid task state: {}", err)))?;
842+
843+
if task_state == TaskState::Completed
844+
|| task_state == TaskState::Failed
845+
|| task_state == TaskState::Cancelled
846+
{
821847
return Ok(());
822848
}
823849

0 commit comments

Comments
 (0)