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