1use crate::core::{resolve_in_dir, sort_processes, Process, ProcessStatus, SortKey};
10use crate::error::Result;
11use crate::ui::Printer;
12use clap::Args;
13use std::path::PathBuf;
14
15#[derive(Args, Debug)]
17pub struct ByCommand {
18 pub name: String,
20
21 #[arg(long = "in", short = 'i', num_args = 0..=1, default_missing_value = ".")]
23 pub in_dir: Option<String>,
24
25 #[arg(long)]
27 pub min_cpu: Option<f32>,
28
29 #[arg(long)]
31 pub min_mem: Option<f64>,
32
33 #[arg(long)]
35 pub status: Option<String>,
36
37 #[arg(long)]
39 pub min_uptime: Option<u64>,
40
41 #[arg(long)]
43 pub parent: Option<u32>,
44
45 #[arg(long, short = 'j')]
47 pub json: bool,
48
49 #[arg(long, short = 'v')]
51 pub verbose: bool,
52
53 #[arg(long, short = 'n')]
55 pub limit: Option<usize>,
56
57 #[arg(long, short = 's', value_enum, default_value_t = SortKey::Cpu)]
59 pub sort: SortKey,
60}
61
62impl ByCommand {
63 pub fn execute(&self) -> Result<()> {
65 let printer = Printer::from_flags(self.json, self.verbose);
66
67 let mut processes = Process::find_by_name(&self.name)?;
69
70 let in_dir_filter = resolve_in_dir(&self.in_dir);
72
73 processes.retain(|p| {
75 if let Some(ref dir_path) = in_dir_filter {
77 if let Some(ref proc_cwd) = p.cwd {
78 let proc_path = PathBuf::from(proc_cwd);
79 if !proc_path.starts_with(dir_path) {
80 return false;
81 }
82 } else {
83 return false;
84 }
85 }
86
87 if let Some(min_cpu) = self.min_cpu {
89 if p.cpu_percent < min_cpu {
90 return false;
91 }
92 }
93
94 if let Some(min_mem) = self.min_mem {
96 if p.memory_mb < min_mem {
97 return false;
98 }
99 }
100
101 if let Some(ref status) = self.status {
103 let status_match = match status.to_lowercase().as_str() {
104 "running" => matches!(p.status, ProcessStatus::Running),
105 "sleeping" | "sleep" => matches!(p.status, ProcessStatus::Sleeping),
106 "stopped" | "stop" => matches!(p.status, ProcessStatus::Stopped),
107 "zombie" => matches!(p.status, ProcessStatus::Zombie),
108 _ => true,
109 };
110 if !status_match {
111 return false;
112 }
113 }
114
115 if let Some(min_uptime) = self.min_uptime {
117 if let Some(start_time) = p.start_time {
118 let now = std::time::SystemTime::now()
119 .duration_since(std::time::UNIX_EPOCH)
120 .map(|d| d.as_secs())
121 .unwrap_or(0);
122 if now.saturating_sub(start_time) < min_uptime {
123 return false;
124 }
125 } else {
126 return false;
127 }
128 }
129
130 if let Some(ppid) = self.parent {
132 if p.parent_pid != Some(ppid) {
133 return false;
134 }
135 }
136
137 true
138 });
139
140 sort_processes(&mut processes, self.sort);
142
143 if let Some(limit) = self.limit {
145 processes.truncate(limit);
146 }
147
148 let mut context_parts = vec![format!("by '{}'", self.name)];
150 if let Some(ref dir) = in_dir_filter {
151 context_parts.push(format!("in {}", dir.display()));
152 }
153 let context = Some(context_parts.join(" "));
154
155 printer.print_processes_as("by", &processes, context.as_deref());
156 Ok(())
157 }
158}