lean_ctx/core/workflow/
store.rs1use crate::core::workflow::types::WorkflowRun;
2use std::path::PathBuf;
3
4fn active_workflow_path() -> Option<PathBuf> {
5 crate::core::data_dir::lean_ctx_data_dir()
6 .ok()
7 .map(|d| d.join("workflows").join("active.json"))
8}
9
10const STALE_MINUTES: i64 = 30;
12
13pub fn load_active() -> Result<Option<WorkflowRun>, String> {
14 let Some(path) = active_workflow_path() else {
15 return Ok(None);
16 };
17 let content = match std::fs::read_to_string(&path) {
18 Ok(c) => c,
19 Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None),
20 Err(e) => return Err(format!("read {}: {e}", path.display())),
21 };
22 let run: WorkflowRun =
23 serde_json::from_str(&content).map_err(|e| format!("Invalid workflow JSON: {e}"))?;
24
25 let elapsed = chrono::Utc::now()
26 .signed_duration_since(run.updated_at)
27 .num_minutes();
28 if elapsed > STALE_MINUTES || run.current == "done" {
29 let _ = std::fs::remove_file(&path);
30 return Ok(None);
31 }
32 Ok(Some(run))
33}
34
35pub fn save_active(run: &WorkflowRun) -> Result<(), String> {
36 let Some(path) = active_workflow_path() else {
37 return Err("No home directory available".to_string());
38 };
39 if let Some(parent) = path.parent() {
40 std::fs::create_dir_all(parent).map_err(|e| format!("mkdir failed: {e}"))?;
41 }
42 let json = serde_json::to_string_pretty(run).map_err(|e| format!("serialize failed: {e}"))?;
43 let tmp = path.with_extension("tmp");
44 std::fs::write(&tmp, json).map_err(|e| format!("write failed: {e}"))?;
45 std::fs::rename(&tmp, &path).map_err(|e| format!("rename failed: {e}"))?;
46 Ok(())
47}
48
49pub fn clear_active() -> Result<(), String> {
50 let Some(path) = active_workflow_path() else {
51 return Ok(());
52 };
53 match std::fs::remove_file(&path) {
54 Ok(()) => Ok(()),
55 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
56 Err(e) => Err(format!("remove {}: {e}", path.display())),
57 }
58}