proc_cli/commands/
kill.rs1use crate::core::{parse_targets, resolve_targets_excluding_self, 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);
57 let (processes, not_found) = resolve_targets_excluding_self(&targets);
58
59 for target in ¬_found {
61 printer.warning(&format!("Target not found: {}", target));
62 }
63
64 if processes.is_empty() {
65 return Err(ProcError::ProcessNotFound(self.target.clone()));
66 }
67
68 if self.dry_run {
70 printer.print_processes(&processes);
71 printer.warning(&format!(
72 "Dry run: would kill {} process{}",
73 processes.len(),
74 if processes.len() == 1 { "" } else { "es" }
75 ));
76 return Ok(());
77 }
78
79 if !self.yes && !self.json {
81 self.print_confirmation_prompt(&processes);
82
83 let confirmed = Confirm::new()
84 .with_prompt(format!(
85 "Kill {} process{}?",
86 processes.len(),
87 if processes.len() == 1 { "" } else { "es" }
88 ))
89 .default(false)
90 .interact()
91 .unwrap_or(false);
92
93 if !confirmed {
94 printer.warning("Cancelled");
95 return Ok(());
96 }
97 }
98
99 let mut killed = Vec::new();
101 let mut failed = Vec::new();
102
103 for proc in processes {
104 let result = if self.graceful {
105 proc.terminate()
106 } else {
107 proc.kill()
108 };
109
110 match result {
111 Ok(()) => killed.push(proc),
112 Err(e) => failed.push((proc, e.to_string())),
113 }
114 }
115
116 printer.print_kill_result(&killed, &failed);
117
118 if failed.is_empty() {
119 Ok(())
120 } else {
121 Err(ProcError::SignalError(format!(
122 "Failed to kill {} process(es)",
123 failed.len()
124 )))
125 }
126 }
127
128 fn print_confirmation_prompt(&self, processes: &[Process]) {
129 use colored::*;
130
131 println!(
132 "\n{} Found {} process{} to kill:\n",
133 "⚠".yellow().bold(),
134 processes.len().to_string().cyan().bold(),
135 if processes.len() == 1 { "" } else { "es" }
136 );
137
138 for proc in processes {
139 println!(
140 " {} {} [PID {}] - CPU: {:.1}%, MEM: {:.1}MB",
141 "→".bright_black(),
142 proc.name.white().bold(),
143 proc.pid.to_string().cyan(),
144 proc.cpu_percent,
145 proc.memory_mb
146 );
147 }
148 println!();
149 }
150}