Skip to main content

lean_ctx/core/workflow/
store.rs

1use 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
10/// Stale threshold: workflows inactive for over 30 minutes are auto-cleared on load.
11const 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}