Skip to main content

actr_cli/commands/
rm.rs

1use crate::commands::process::{kill_process, wait_for_exit};
2use crate::commands::runtime_state::{RuntimeStateStore, RuntimeStatus, resolve_hyper_dir};
3use crate::core::{Command, CommandContext, CommandResult, ComponentType};
4use crate::error::ActrCliError;
5use anyhow::Result;
6use async_trait::async_trait;
7use clap::Args;
8use std::path::PathBuf;
9use std::time::Duration;
10
11#[derive(Args, Debug)]
12pub struct RmCommand {
13    /// WID or WID prefix (min 8 chars)
14    #[arg(value_name = "WID")]
15    pub wid: String,
16
17    /// Runtime configuration file
18    #[arg(short = 'c', long = "config", value_name = "FILE")]
19    pub config: Option<PathBuf>,
20
21    /// Hyper data directory
22    #[arg(long = "hyper-dir", value_name = "DIR")]
23    pub hyper_dir: Option<PathBuf>,
24
25    /// Force remove even if workload is running
26    #[arg(short = 'f', long = "force")]
27    pub force: bool,
28}
29
30#[async_trait]
31impl Command for RmCommand {
32    async fn execute(&self, _ctx: &CommandContext) -> Result<CommandResult> {
33        let hyper_dir = resolve_hyper_dir(self.config.as_deref(), self.hyper_dir.as_deref())?;
34        let store = RuntimeStateStore::new(hyper_dir);
35        let entry = store.resolve_wid_prefix(&self.wid).await?;
36
37        if entry.status == RuntimeStatus::Running {
38            if !self.force {
39                return Err(ActrCliError::command_error(format!(
40                    "Workload {} is running. Stop it first or use -f to force remove.",
41                    entry.wid_short()
42                ))
43                .into());
44            }
45
46            if kill_process(entry.record.pid)?
47                && !wait_for_exit(entry.record.pid, Duration::from_secs(1)).await
48            {
49                return Err(ActrCliError::command_error(format!(
50                    "Process {} did not exit after SIGKILL",
51                    entry.record.pid
52                ))
53                .into());
54            }
55        }
56
57        store.delete_record_by_wid(&entry.record.wid).await?;
58        println!("Removed {}", entry.wid_short());
59        Ok(CommandResult::Success(String::new()))
60    }
61
62    fn required_components(&self) -> Vec<ComponentType> {
63        vec![]
64    }
65
66    fn name(&self) -> &str {
67        "rm"
68    }
69
70    fn description(&self) -> &str {
71        "Remove a detached runtime instance record"
72    }
73}