window_enumerator/
utils.rs1use crate::errors::{Result, WindowError};
2use crate::types::WindowInfo;
3
4#[cfg(feature = "selection")]
5use crate::types::Selection;
6
7#[cfg(feature = "sorting")]
8use crate::types::PositionSort; #[cfg(feature = "selection")]
24#[allow(dead_code)] pub fn parse_selection(selection_str: &str) -> Result<Selection> {
26 let selection_str = selection_str.trim().to_lowercase();
27
28 if selection_str == "all" {
29 return Ok(Selection::All);
30 }
31
32 let mut indices = Vec::new();
33 let parts: Vec<&str> = selection_str.split(',').collect();
34
35 for part in parts {
36 let part = part.trim();
37 if part.contains('-') {
38 let range_parts: Vec<&str> = part.split('-').collect();
40 if range_parts.len() == 2 {
41 let start = parse_index(range_parts[0].trim())?;
42 let end = parse_index(range_parts[1].trim())?;
43
44 for i in start..=end {
45 indices.push(i);
46 }
47 } else {
48 return Err(WindowError::InvalidRange);
49 }
50 } else {
51 let index = parse_index(part)?;
53 indices.push(index);
54 }
55 }
56
57 indices.sort();
59 indices.dedup();
60
61 Ok(Selection::Indices(indices))
62}
63
64#[cfg(feature = "sorting")]
78#[allow(dead_code)] pub fn parse_position_sort(sort_str: &str) -> Result<Option<PositionSort>> {
80 let sort_str = sort_str.trim().to_lowercase();
81
82 if sort_str.is_empty() {
83 return Ok(None);
84 }
85
86 if sort_str.contains('|') {
87 let parts: Vec<&str> = sort_str.split('|').collect();
89 if parts.len() != 2 {
90 return Err(WindowError::InvalidPositionSortFormat);
91 }
92
93 let x_part = parts[0].trim();
94 let y_part = parts[1].trim();
95
96 let x_order = parse_single_position_order(x_part, 'x')?;
97 let y_order = parse_single_position_order(y_part, 'y')?;
98
99 Ok(Some(PositionSort::XY(x_order, y_order)))
100 } else {
101 if sort_str.starts_with('x') {
103 let order = parse_single_position_order(&sort_str, 'x')?;
104 Ok(Some(PositionSort::X(order)))
105 } else if sort_str.starts_with('y') {
106 let order = parse_single_position_order(&sort_str, 'y')?;
107 Ok(Some(PositionSort::Y(order)))
108 } else {
109 Err(WindowError::InvalidPositionSortFormat)
110 }
111 }
112}
113
114#[cfg(feature = "sorting")]
116#[allow(dead_code)] fn parse_single_position_order(part: &str, expected_prefix: char) -> Result<i8> {
118 if part.len() < 2 || !part.starts_with(expected_prefix) {
119 return Err(WindowError::InvalidPositionSortFormat);
120 }
121
122 let order_str = &part[1..];
123 match order_str {
124 "1" => Ok(1),
125 "-1" => Ok(-1),
126 _ => Err(WindowError::InvalidSortOrder),
127 }
128}
129
130#[allow(dead_code)] fn parse_index(s: &str) -> Result<usize> {
133 s.parse().map_err(|_| WindowError::InvalidIndex)
134}
135
136pub fn matches_criteria(window: &WindowInfo, criteria: &crate::types::FilterCriteria) -> bool {
147 if let Some(pid) = criteria.pid {
149 if window.pid != pid {
150 return false;
151 }
152 }
153
154 if let Some(ref title_filter) = criteria.title_contains {
156 if !title_filter.is_empty()
157 && !window
158 .title
159 .to_lowercase()
160 .contains(&title_filter.to_lowercase())
161 {
162 return false;
163 }
164 }
165
166 if let Some(ref class_filter) = criteria.class_name_contains {
168 if !class_filter.is_empty()
169 && !window
170 .class_name
171 .to_lowercase()
172 .contains(&class_filter.to_lowercase())
173 {
174 return false;
175 }
176 }
177
178 if let Some(ref process_filter) = criteria.process_name_contains {
180 if !process_filter.is_empty()
181 && !window
182 .process_name
183 .to_lowercase()
184 .contains(&process_filter.to_lowercase())
185 {
186 return false;
187 }
188 }
189
190 if let Some(ref file_filter) = criteria.process_file_contains {
192 if !file_filter.is_empty() {
193 let file_str = window.process_file.to_string_lossy().to_lowercase();
194 if !file_str.contains(&file_filter.to_lowercase()) {
195 return false;
196 }
197 }
198 }
199
200 true
201}