Skip to main content

ralph/cli/queue/
archive.rs

1//! Queue archive subcommand.
2//!
3//! Responsibilities:
4//! - Move terminal tasks (done/rejected) from queue.json to done.json.
5//! - Support dry-run mode to preview what would be archived.
6//!
7//! Not handled here:
8//! - Automatic archiving based on task age (see `queue.auto_archive_terminal_after_days`).
9//! - Archive internals (see `crate::queue::operations::archive`).
10//!
11//! Invariants/assumptions:
12//! - Dry-run mode does NOT create undo snapshots or write to disk.
13//! - Normal mode creates an undo snapshot before mutation.
14
15use anyhow::Result;
16use clap::Args;
17
18use crate::config::Resolved;
19use crate::queue;
20
21/// Arguments for `ralph queue archive`.
22#[derive(Args)]
23#[command(
24    after_long_help = "Examples:\n  ralph queue archive\n  ralph queue archive --dry-run\n\nDry run:\n  Shows what would be archived without modifying files."
25)]
26pub struct QueueArchiveArgs {
27    /// Show what would be archived without writing to disk.
28    #[arg(long)]
29    pub dry_run: bool,
30}
31
32pub(crate) fn handle(resolved: &Resolved, force: bool, args: QueueArchiveArgs) -> Result<()> {
33    let _queue_lock = queue::acquire_queue_lock(&resolved.repo_root, "queue archive", force)?;
34
35    // Create undo snapshot before mutation (only if not dry-run)
36    if !args.dry_run {
37        crate::undo::create_undo_snapshot(resolved, "queue archive")?;
38    }
39
40    let max_depth = resolved.config.queue.max_dependency_depth.unwrap_or(10);
41
42    if args.dry_run {
43        // Dry-run: compute what would be archived without modifying files
44        let mut active = queue::load_queue(&resolved.queue_path)?;
45        let mut done = queue::load_queue_or_default(&resolved.done_path)?;
46        let now = crate::timeutil::now_utc_rfc3339()?;
47        let report = queue::archive_terminal_tasks_in_memory(&mut active, &mut done, &now)?;
48
49        if report.moved_ids.is_empty() {
50            log::info!("Dry run: no terminal tasks to archive.");
51        } else {
52            log::info!(
53                "Dry run: would archive {} terminal task(s).",
54                report.moved_ids.len()
55            );
56            log::info!("Task IDs: {}", report.moved_ids.join(", "));
57        }
58    } else {
59        // Normal execution
60        let report = queue::archive_terminal_tasks(
61            &resolved.queue_path,
62            &resolved.done_path,
63            &resolved.id_prefix,
64            resolved.id_width,
65            max_depth,
66        )?;
67        if report.moved_ids.is_empty() {
68            log::info!("No terminal tasks (done/rejected) to archive.");
69        } else {
70            log::info!(
71                "Archived {} terminal task(s) (done/rejected).",
72                report.moved_ids.len()
73            );
74        }
75    }
76    Ok(())
77}