fix cleaning 🐛

This commit is contained in:
Andreas Bigger
2025-12-29 17:39:03 -05:00
parent 219b2a0e1d
commit 73a711bb06
4 changed files with 77 additions and 14 deletions
+1
View File
@@ -81,6 +81,7 @@ chrono = { version = "0.4", features = ["serde"] }
# CLI
clap = { version = "4.5", features = ["derive"] }
indicatif = "0.17"
inquire = "0.7"
# Error handling
thiserror = "2.0"
+1
View File
@@ -31,5 +31,6 @@ tokio = { workspace = true }
futures = { workspace = true }
clap = { workspace = true }
indicatif = { workspace = true }
inquire = { workspace = true }
anyhow = { workspace = true }
chrono = { workspace = true }
+62 -3
View File
@@ -1,6 +1,7 @@
//! Background job status command.
use anyhow::{Context, Result};
use inquire::Select;
use paracas_daemon::{DownloadJob, JobStatus, StateManager};
/// Execute the status command.
@@ -15,8 +16,11 @@ pub(crate) fn status(
StateManager::with_default_path().context("Failed to initialize state manager")?;
// Handle cancellation request
// cancel_id is Some("") when --cancel is passed without a value
// cancel_id is Some(id) when --cancel <id> is passed
if let Some(id) = cancel_id {
return cancel_job(&state_manager, id);
let id_opt = if id.is_empty() { None } else { Some(id) };
return cancel_job(&state_manager, id_opt);
}
// Handle follow/watch mode
@@ -133,8 +137,63 @@ fn list_jobs(state: &StateManager, running_only: bool, show_all: bool) -> Result
Ok(())
}
fn cancel_job(state: &StateManager, job_id: &str) -> Result<()> {
let id = job_id.parse().context("Invalid job ID format")?;
/// Prompt user to select a job from available cancellable jobs.
fn prompt_cancel_selection(state: &StateManager) -> Result<String> {
let jobs = state.list_jobs()?;
let filtered: Vec<_> = jobs
.into_iter()
.filter(|job| matches!(job.status, JobStatus::Running | JobStatus::Pending))
.collect();
if filtered.is_empty() {
anyhow::bail!("No running or pending jobs found to cancel.");
}
let options: Vec<String> = filtered
.iter()
.map(|job| {
let instruments: Vec<_> = job.tasks.iter().map(|t| t.instrument_id.as_str()).collect();
let instruments_display = if instruments.len() > 3 {
format!(
"{}, ... (+{} more)",
instruments[..3].join(", "),
instruments.len() - 3
)
} else {
instruments.join(", ")
};
format!(
"{} | {:?} | {:.1}% | {}",
job.id,
job.status,
job.progress_percent(),
instruments_display
)
})
.collect();
let selection = Select::new("Select a job to cancel:", options)
.prompt()
.context("Job selection cancelled")?;
// Extract the job ID from the selection (first part before " | ")
let job_id = selection
.split(" | ")
.next()
.context("Failed to parse job selection")?
.to_string();
Ok(job_id)
}
fn cancel_job(state: &StateManager, job_id: Option<&str>) -> Result<()> {
let id_str = match job_id {
Some(id) => id.to_string(),
None => prompt_cancel_selection(state)?,
};
let id = id_str.parse().context("Invalid job ID format")?;
let mut job: DownloadJob = state.load_job(id).context("Job not found")?;
+13 -11
View File
@@ -104,8 +104,8 @@ enum Commands {
#[arg(short, long)]
follow: Option<u64>,
/// Cancel a running job
#[arg(long)]
/// Cancel a running job (prompts for selection if no job ID provided)
#[arg(long, num_args = 0..=1, default_missing_value = "")]
cancel: Option<String>,
},
@@ -164,20 +164,20 @@ enum Commands {
enum JobAction {
/// Pause a running job
Pause {
/// Job ID to pause
job_id: String,
/// Job ID to pause (if omitted, prompts for selection)
job_id: Option<String>,
},
/// Resume a paused job
Resume {
/// Job ID to resume
job_id: String,
/// Job ID to resume (if omitted, prompts for selection)
job_id: Option<String>,
},
/// Kill a running or paused job
Kill {
/// Job ID to kill
job_id: String,
/// Job ID to kill (if omitted, prompts for selection)
job_id: Option<String>,
},
/// Clean up finished jobs from storage
@@ -269,12 +269,14 @@ async fn main() -> Result<()> {
}
Commands::Job { action } => match action {
JobAction::Pause { job_id } => {
commands::job::job_command("pause", Some(&job_id), false)
commands::job::job_command("pause", job_id.as_deref(), false)
}
JobAction::Resume { job_id } => {
commands::job::job_command("resume", Some(&job_id), false)
commands::job::job_command("resume", job_id.as_deref(), false)
}
JobAction::Kill { job_id } => {
commands::job::job_command("kill", job_id.as_deref(), false)
}
JobAction::Kill { job_id } => commands::job::job_command("kill", Some(&job_id), false),
JobAction::Clean { all } => commands::job::job_command("clean", None, all),
},
}