proc_cli/commands/
kill.rs1use crate::core::{resolve_target, Process};
10use crate::error::{ProcError, Result};
11use crate::ui::{OutputFormat, Printer};
12use clap::Args;
13use dialoguer::Confirm;
14
15#[derive(Args, Debug)]
17pub struct KillCommand {
18 pub target: String,
20
21 #[arg(long, short = 'y')]
23 pub yes: bool,
24
25 #[arg(long)]
27 pub dry_run: bool,
28
29 #[arg(long, short = 'j')]
31 pub json: bool,
32
33 #[arg(long, short = 'v')]
35 pub verbose: bool,
36
37 #[arg(long, short = 'g')]
39 pub graceful: bool,
40}
41
42impl KillCommand {
43 pub fn execute(&self) -> Result<()> {
44 let format = if self.json {
45 OutputFormat::Json
46 } else {
47 OutputFormat::Human
48 };
49 let printer = Printer::new(format, self.verbose);
50
51 let processes = self.resolve_target_processes()?;
53
54 if processes.is_empty() {
55 return Err(ProcError::ProcessNotFound(self.target.clone()));
56 }
57
58 if self.dry_run {
60 printer.warning(&format!(
61 "Dry run: would kill {} process{}",
62 processes.len(),
63 if processes.len() == 1 { "" } else { "es" }
64 ));
65 printer.print_processes(&processes);
66 return Ok(());
67 }
68
69 if !self.yes && !self.json {
71 self.print_confirmation_prompt(&processes);
72
73 let confirmed = Confirm::new()
74 .with_prompt(format!(
75 "Kill {} process{}?",
76 processes.len(),
77 if processes.len() == 1 { "" } else { "es" }
78 ))
79 .default(false)
80 .interact()
81 .unwrap_or(false);
82
83 if !confirmed {
84 printer.warning("Cancelled");
85 return Ok(());
86 }
87 }
88
89 let mut killed = Vec::new();
91 let mut failed = Vec::new();
92
93 for proc in processes {
94 let result = if self.graceful {
95 proc.terminate()
96 } else {
97 proc.kill()
98 };
99
100 match result {
101 Ok(()) => killed.push(proc),
102 Err(e) => failed.push((proc, e.to_string())),
103 }
104 }
105
106 printer.print_kill_result(&killed, &failed);
107
108 if failed.is_empty() {
109 Ok(())
110 } else {
111 Err(ProcError::SignalError(format!(
112 "Failed to kill {} process(es)",
113 failed.len()
114 )))
115 }
116 }
117
118 fn resolve_target_processes(&self) -> Result<Vec<Process>> {
120 resolve_target(&self.target)
121 }
122
123 fn print_confirmation_prompt(&self, processes: &[Process]) {
124 use colored::*;
125
126 println!(
127 "\n{} Found {} process{} to kill:\n",
128 "⚠".yellow().bold(),
129 processes.len().to_string().cyan().bold(),
130 if processes.len() == 1 { "" } else { "es" }
131 );
132
133 for proc in processes {
134 println!(
135 " {} {} [PID {}] - CPU: {:.1}%, MEM: {:.1}MB",
136 "→".bright_black(),
137 proc.name.white().bold(),
138 proc.pid.to_string().cyan(),
139 proc.cpu_percent,
140 proc.memory_mb
141 );
142 }
143 println!();
144 }
145}