use std::time::Duration;
use crate::error::Result;
use super::{ProcessInfo, ProcessPriority, list_processes};
pub async fn find_process_by_pid(pid: u32) -> Result<Option<ProcessInfo>> {
let processes = list_processes().await?;
Ok(processes.into_iter().find(|p| p.pid == pid))
}
pub async fn find_processes_by_name(name: &str) -> Result<Vec<ProcessInfo>> {
let processes = list_processes().await?;
let name_lower = name.to_lowercase();
Ok(processes
.into_iter()
.filter(|p| p.name.to_lowercase().contains(&name_lower))
.collect())
}
pub async fn find_process_by_name(name: &str) -> Result<Option<ProcessInfo>> {
let processes = find_processes_by_name(name).await?;
Ok(processes.into_iter().next())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SortOrder {
#[default]
None,
Pid,
MemoryDesc,
CpuDesc,
}
#[derive(Default, Debug, Clone)]
pub struct ProcessFilter {
name_contains: Option<String>,
min_memory_bytes: Option<u64>,
max_memory_bytes: Option<u64>,
min_threads: Option<u32>,
parent_pid: Option<u32>,
priority: Option<ProcessPriority>,
min_cpu_time: Option<Duration>,
max_cpu_time: Option<Duration>,
sort: SortOrder,
}
impl ProcessFilter {
pub fn new() -> Self {
Self::default()
}
pub fn name_contains<S: Into<String>>(mut self, name: S) -> Self {
self.name_contains = Some(name.into().to_lowercase());
self
}
pub fn min_memory(mut self, bytes: u64) -> Self {
self.min_memory_bytes = Some(bytes);
self
}
pub fn max_memory(mut self, bytes: u64) -> Self {
self.max_memory_bytes = Some(bytes);
self
}
pub fn min_threads(mut self, count: u32) -> Self {
self.min_threads = Some(count);
self
}
pub fn parent_pid(mut self, pid: u32) -> Self {
self.parent_pid = Some(pid);
self
}
pub fn priority(mut self, p: ProcessPriority) -> Self {
self.priority = Some(p);
self
}
pub fn min_cpu_time(mut self, d: Duration) -> Self {
self.min_cpu_time = Some(d);
self
}
pub fn max_cpu_time(mut self, d: Duration) -> Self {
self.max_cpu_time = Some(d);
self
}
pub fn sort_by_pid(mut self) -> Self {
self.sort = SortOrder::Pid;
self
}
pub fn sort_by_memory(mut self) -> Self {
self.sort = SortOrder::MemoryDesc;
self
}
pub fn sort_by_cpu(mut self) -> Self {
self.sort = SortOrder::CpuDesc;
self
}
pub async fn list(self) -> Result<Vec<ProcessInfo>> {
let processes = list_processes().await?;
let mut results: Vec<ProcessInfo> = processes
.into_iter()
.filter(|p| self.matches(p))
.collect();
match self.sort {
SortOrder::None => {}
SortOrder::Pid => results.sort_by_key(|p| p.pid),
SortOrder::MemoryDesc => results.sort_by(|a, b| {
b.memory_usage
.unwrap_or(0)
.cmp(&a.memory_usage.unwrap_or(0))
}),
SortOrder::CpuDesc => results.sort_by(|a, b| {
let a_cpu = a.total_cpu_time().map(|d| d.as_nanos()).unwrap_or(0);
let b_cpu = b.total_cpu_time().map(|d| d.as_nanos()).unwrap_or(0);
b_cpu.cmp(&a_cpu)
}),
}
Ok(results)
}
fn matches(&self, process: &ProcessInfo) -> bool {
if let Some(ref name) = self.name_contains {
if !process.name.to_lowercase().contains(name) {
return false;
}
}
if let Some(min_mem) = self.min_memory_bytes {
if process.memory_usage.map_or(true, |m| m < min_mem) {
return false;
}
}
if let Some(max_mem) = self.max_memory_bytes {
if process.memory_usage.map_or(false, |m| m > max_mem) {
return false;
}
}
if let Some(min_threads) = self.min_threads {
if process.thread_count.map_or(true, |t| t < min_threads) {
return false;
}
}
if let Some(parent) = self.parent_pid {
if process.parent_pid != Some(parent) {
return false;
}
}
if let Some(ref priority) = self.priority {
if process.priority_class.as_ref() != Some(priority) {
return false;
}
}
if let Some(min_cpu) = self.min_cpu_time {
if process.total_cpu_time().map_or(true, |t| t < min_cpu) {
return false;
}
}
if let Some(max_cpu) = self.max_cpu_time {
if process.total_cpu_time().map_or(false, |t| t > max_cpu) {
return false;
}
}
true
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use super::*;
fn make_process(pid: u32, name: &str) -> ProcessInfo {
ProcessInfo::basic(pid, name.to_string())
}
#[test]
fn test_filter_builder_name() {
let filter = ProcessFilter::new().name_contains("test");
let mut p = make_process(1, "test.exe");
p.memory_usage = Some(2048);
p.thread_count = Some(3);
assert!(filter.matches(&p));
let p2 = make_process(2, "other.exe");
assert!(!filter.matches(&p2));
}
#[test]
fn test_filter_memory_range() {
let filter = ProcessFilter::new().min_memory(1024).max_memory(4096);
let mut p = make_process(1, "a.exe");
p.memory_usage = Some(2048);
assert!(filter.matches(&p));
let mut too_small = make_process(2, "b.exe");
too_small.memory_usage = Some(512);
assert!(!filter.matches(&too_small));
let mut too_large = make_process(3, "c.exe");
too_large.memory_usage = Some(8192);
assert!(!filter.matches(&too_large));
}
#[test]
fn test_filter_priority() {
let filter = ProcessFilter::new().priority(ProcessPriority::Normal);
let mut p_normal = make_process(1, "a.exe");
p_normal.priority_class = Some(ProcessPriority::Normal);
assert!(filter.matches(&p_normal));
let mut p_high = make_process(2, "b.exe");
p_high.priority_class = Some(ProcessPriority::High);
assert!(!filter.matches(&p_high));
let p_unknown = make_process(3, "c.exe");
assert!(!filter.matches(&p_unknown));
}
#[test]
fn test_filter_cpu_time() {
let min = Duration::from_secs(5);
let filter = ProcessFilter::new().min_cpu_time(min);
let mut p_long = make_process(1, "a.exe");
p_long.user_cpu_time = Some(Duration::from_secs(10));
p_long.kernel_cpu_time = Some(Duration::from_secs(0));
assert!(filter.matches(&p_long));
let mut p_short = make_process(2, "b.exe");
p_short.user_cpu_time = Some(Duration::from_secs(2));
p_short.kernel_cpu_time = Some(Duration::from_secs(1));
assert!(!filter.matches(&p_short));
let p_none = make_process(3, "c.exe");
assert!(!filter.matches(&p_none));
}
#[test]
fn test_filter_max_cpu_time_no_data_passes() {
let max = Duration::from_secs(10);
let filter = ProcessFilter::new().max_cpu_time(max);
let p_none = make_process(1, "a.exe");
assert!(filter.matches(&p_none));
}
#[test]
fn test_sort_order_default() {
let filter = ProcessFilter::new();
assert_eq!(filter.sort, SortOrder::None);
}
}