1use crate::core::{Process, ProcessStatus};
10use crate::error::Result;
11use crate::ui::{OutputFormat, 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, short = 'j')]
39 pub json: bool,
40
41 #[arg(long, short = 'v')]
43 pub verbose: bool,
44
45 #[arg(long, short = 'n')]
47 pub limit: Option<usize>,
48
49 #[arg(long, short = 's', default_value = "cpu")]
51 pub sort: String,
52}
53
54impl ByCommand {
55 pub fn execute(&self) -> Result<()> {
57 let format = if self.json {
58 OutputFormat::Json
59 } else {
60 OutputFormat::Human
61 };
62 let printer = Printer::new(format, self.verbose);
63
64 let mut processes = Process::find_by_name(&self.name)?;
66
67 let in_dir_filter: Option<PathBuf> = self.in_dir.as_ref().map(|p| {
69 if p == "." {
70 std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
71 } else {
72 let path = PathBuf::from(p);
73 if path.is_relative() {
74 std::env::current_dir()
75 .unwrap_or_else(|_| PathBuf::from("."))
76 .join(path)
77 } else {
78 path
79 }
80 }
81 });
82
83 processes.retain(|p| {
85 if let Some(ref dir_path) = in_dir_filter {
87 if let Some(ref proc_cwd) = p.cwd {
88 let proc_path = PathBuf::from(proc_cwd);
89 if !proc_path.starts_with(dir_path) {
90 return false;
91 }
92 } else {
93 return false;
94 }
95 }
96
97 if let Some(min_cpu) = self.min_cpu {
99 if p.cpu_percent < min_cpu {
100 return false;
101 }
102 }
103
104 if let Some(min_mem) = self.min_mem {
106 if p.memory_mb < min_mem {
107 return false;
108 }
109 }
110
111 if let Some(ref status) = self.status {
113 let status_match = match status.to_lowercase().as_str() {
114 "running" => matches!(p.status, ProcessStatus::Running),
115 "sleeping" | "sleep" => matches!(p.status, ProcessStatus::Sleeping),
116 "stopped" | "stop" => matches!(p.status, ProcessStatus::Stopped),
117 "zombie" => matches!(p.status, ProcessStatus::Zombie),
118 _ => true,
119 };
120 if !status_match {
121 return false;
122 }
123 }
124
125 true
126 });
127
128 match self.sort.to_lowercase().as_str() {
130 "cpu" => processes.sort_by(|a, b| {
131 b.cpu_percent
132 .partial_cmp(&a.cpu_percent)
133 .unwrap_or(std::cmp::Ordering::Equal)
134 }),
135 "mem" | "memory" => processes.sort_by(|a, b| {
136 b.memory_mb
137 .partial_cmp(&a.memory_mb)
138 .unwrap_or(std::cmp::Ordering::Equal)
139 }),
140 "pid" => processes.sort_by_key(|p| p.pid),
141 "name" => processes.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase())),
142 _ => {} }
144
145 if let Some(limit) = self.limit {
147 processes.truncate(limit);
148 }
149
150 let mut context_parts = vec![format!("by '{}'", self.name)];
152 if let Some(ref dir) = in_dir_filter {
153 context_parts.push(format!("in {}", dir.display()));
154 }
155 let context = Some(context_parts.join(" "));
156
157 printer.print_processes_with_context(&processes, context.as_deref());
158 Ok(())
159 }
160}