1use crate::error::{Result, SelError};
4use std::collections::BTreeSet;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum Selector {
9 All,
11
12 LineNumbers(Vec<LineSpec>),
14
15 Positions(Vec<Position>),
17}
18
19fn merge_ranges(mut ranges: Vec<(usize, usize)>) -> Vec<(usize, usize)> {
21 if ranges.is_empty() {
22 return ranges;
23 }
24
25 ranges.sort_by_key(|&(start, _)| start);
26 let mut merged = Vec::new();
27 let mut current_start = ranges[0].0;
28 let mut current_end = ranges[0].1;
29
30 for &(start, end) in &ranges[1..] {
31 if start <= current_end + 1 {
32 current_end = current_end.max(end);
34 } else {
35 merged.push((current_start, current_end));
36 current_start = start;
37 current_end = end;
38 }
39 }
40 merged.push((current_start, current_end));
41
42 merged
43}
44
45fn merge_lines_with_ranges(lines: Vec<usize>, ranges: Vec<(usize, usize)>) -> Vec<LineSpec> {
50 let mut all_ranges: Vec<(usize, usize)> = ranges;
53
54 for line in lines {
56 all_ranges.push((line, line));
57 }
58
59 let merged = merge_ranges(all_ranges);
61
62 merged
64 .into_iter()
65 .map(|(start, end)| {
66 if start == end {
67 LineSpec::Single(start)
68 } else {
69 LineSpec::Range(start, end)
70 }
71 })
72 .collect()
73}
74
75impl Selector {
76 pub fn parse(s: &str) -> Result<Self> {
88 if s.is_empty() {
89 return Ok(Selector::All);
90 }
91
92 let has_position = s.contains(':');
94
95 let parts: Vec<&str> = s.split(',').collect();
96
97 if has_position {
98 let mut positions = Vec::new();
100 for part in parts {
101 let pos = Position::parse(part)?;
102 positions.push(pos);
103 }
104 Ok(Selector::Positions(positions))
105 } else {
106 let mut specs = Vec::new();
108 for part in parts {
109 let spec = LineSpec::parse(part)?;
110 specs.push(spec);
111 }
112 Ok(Selector::LineNumbers(specs))
113 }
114 }
115
116 pub fn normalize(&self) -> Self {
118 match self {
119 Selector::All => Selector::All,
120 Selector::Positions(p) => {
121 let unique: BTreeSet<_> = p.iter().collect();
123 Selector::Positions(unique.into_iter().cloned().collect())
124 }
125 Selector::LineNumbers(specs) => {
126 let mut lines: Vec<usize> = Vec::new();
128 let mut ranges: Vec<(usize, usize)> = Vec::new();
129
130 for spec in specs {
131 match spec {
132 LineSpec::Single(n) => lines.push(*n),
133 LineSpec::Range(start, end) => ranges.push((*start, *end)),
134 }
135 }
136
137 lines.sort();
138 lines.dedup();
139 ranges.sort();
140
141 let merged_ranges = merge_ranges(ranges);
143
144 let result = merge_lines_with_ranges(lines, merged_ranges);
146
147 Selector::LineNumbers(result)
148 }
149 }
150 }
151
152 pub fn is_positional(&self) -> bool {
154 matches!(self, Selector::Positions(_))
155 }
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq)]
160pub enum LineSpec {
161 Single(usize),
163
164 Range(usize, usize),
166}
167
168impl LineSpec {
169 pub fn parse(s: &str) -> Result<Self> {
178 if let Some((start, end)) = s.split_once('-') {
179 let start = start.parse::<usize>().map_err(|_| {
180 SelError::InvalidSelector(format!("Invalid range start: '{}'", start))
181 })?;
182 let end = end
183 .parse::<usize>()
184 .map_err(|_| SelError::InvalidSelector(format!("Invalid range end: '{}'", end)))?;
185
186 if start == 0 || end == 0 {
187 return Err(SelError::InvalidSelector(
188 "Line numbers must be >= 1".to_string(),
189 ));
190 }
191
192 if start > end {
193 return Err(SelError::InvalidSelector(format!(
194 "Range start ({}) > end ({})",
195 start, end
196 )));
197 }
198
199 Ok(LineSpec::Range(start, end))
200 } else {
201 let n = s
202 .parse::<usize>()
203 .map_err(|_| SelError::InvalidSelector(format!("Invalid line number: '{}'", s)))?;
204
205 if n == 0 {
206 return Err(SelError::InvalidSelector(
207 "Line number must be >= 1".to_string(),
208 ));
209 }
210
211 Ok(LineSpec::Single(n))
212 }
213 }
214
215 pub fn contains(&self, line: usize) -> bool {
217 match self {
218 LineSpec::Single(n) => *n == line,
219 LineSpec::Range(start, end) => line >= *start && line <= *end,
220 }
221 }
222
223 pub fn start(&self) -> usize {
225 match self {
226 LineSpec::Single(n) => *n,
227 LineSpec::Range(start, _) => *start,
228 }
229 }
230}
231
232#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
234pub struct Position {
235 pub line: usize,
237
238 pub column: usize,
240}
241
242impl Position {
243 pub fn new(line: usize, column: usize) -> Self {
245 Self { line, column }
246 }
247
248 pub fn parse(s: &str) -> Result<Self> {
258 let (line_str, col_str) = s.split_once(':').ok_or_else(|| {
259 SelError::InvalidSelector(format!("Invalid position format: '{}'", s))
260 })?;
261
262 let line = line_str.parse::<usize>().map_err(|_| {
263 SelError::InvalidSelector(format!("Invalid line number in position: '{}'", line_str))
264 })?;
265
266 let column = col_str.parse::<usize>().map_err(|_| {
267 SelError::InvalidSelector(format!("Invalid column number in position: '{}'", col_str))
268 })?;
269
270 if line == 0 || column == 0 {
271 return Err(SelError::InvalidSelector(
272 "Line and column numbers must be >= 1".to_string(),
273 ));
274 }
275
276 Ok(Position { line, column })
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use super::*;
283
284 #[test]
285 fn parse_single_line() {
286 let sel = Selector::parse("42").unwrap();
287 assert_eq!(sel, Selector::LineNumbers(vec![LineSpec::Single(42)]));
288 }
289
290 #[test]
291 fn parse_range() {
292 let sel = Selector::parse("10-20").unwrap();
293 assert_eq!(sel, Selector::LineNumbers(vec![LineSpec::Range(10, 20)]));
294 }
295
296 #[test]
297 fn parse_multiple_specs() {
298 let sel = Selector::parse("1,5,10-15,20").unwrap();
299 assert_eq!(
300 sel,
301 Selector::LineNumbers(vec![
302 LineSpec::Single(1),
303 LineSpec::Single(5),
304 LineSpec::Range(10, 15),
305 LineSpec::Single(20),
306 ])
307 );
308 }
309
310 #[test]
311 fn parse_position() {
312 let sel = Selector::parse("23:260").unwrap();
313 assert_eq!(sel, Selector::Positions(vec![Position::new(23, 260)]));
314 }
315
316 #[test]
317 fn parse_multiple_positions() {
318 let sel = Selector::parse("15:30,23:260").unwrap();
319 assert_eq!(
320 sel,
321 Selector::Positions(vec![Position::new(15, 30), Position::new(23, 260),])
322 );
323 }
324
325 #[test]
326 fn reject_mixed_selector() {
327 let result = Selector::parse("1,23:260");
331 assert!(result.is_err());
332 }
333
334 #[test]
335 fn parse_empty_selector() {
336 let sel = Selector::parse("").unwrap();
337 assert_eq!(sel, Selector::All);
338 }
339
340 #[test]
341 fn reject_zero_line() {
342 assert!(Selector::parse("0").is_err());
343 assert!(Selector::parse("10-0").is_err());
344 }
345
346 #[test]
347 fn reject_invalid_range() {
348 assert!(Selector::parse("20-10").is_err());
349 }
350
351 #[test]
352 fn line_spec_contains() {
353 let spec = LineSpec::Range(10, 20);
354 assert!(!spec.contains(9));
355 assert!(spec.contains(10));
356 assert!(spec.contains(15));
357 assert!(spec.contains(20));
358 assert!(!spec.contains(21));
359 }
360
361 #[test]
362 fn normalize_merges_overlapping_ranges() {
363 let sel = Selector::LineNumbers(vec![LineSpec::Range(1, 5), LineSpec::Range(3, 10)]);
364 let normalized = sel.normalize();
365 assert_eq!(
366 normalized,
367 Selector::LineNumbers(vec![LineSpec::Range(1, 10)])
368 );
369 }
370
371 #[test]
372 fn normalize_merges_adjacent_ranges() {
373 let sel = Selector::LineNumbers(vec![LineSpec::Range(1, 5), LineSpec::Range(6, 10)]);
374 let normalized = sel.normalize();
375 assert_eq!(
376 normalized,
377 Selector::LineNumbers(vec![LineSpec::Range(1, 10)])
378 );
379 }
380
381 #[test]
382 fn normalize_merges_single_lines_into_ranges() {
383 let sel = Selector::LineNumbers(vec![
384 LineSpec::Single(1),
385 LineSpec::Single(2),
386 LineSpec::Single(3),
387 LineSpec::Single(10),
388 ]);
389 let normalized = sel.normalize();
390 assert_eq!(
391 normalized,
392 Selector::LineNumbers(vec![LineSpec::Range(1, 3), LineSpec::Single(10)])
393 );
394 }
395
396 #[test]
397 fn normalize_complex_merge() {
398 let sel = Selector::LineNumbers(vec![
399 LineSpec::Range(1, 5),
400 LineSpec::Single(6),
401 LineSpec::Range(7, 10),
402 LineSpec::Range(15, 20),
403 LineSpec::Single(21),
404 ]);
405 let normalized = sel.normalize();
406 assert_eq!(
407 normalized,
408 Selector::LineNumbers(vec![LineSpec::Range(1, 10), LineSpec::Range(15, 21)])
409 );
410 }
411
412 #[test]
413 fn normalize_keeps_non_adjacent_ranges_separate() {
414 let sel = Selector::LineNumbers(vec![LineSpec::Range(1, 5), LineSpec::Range(10, 15)]);
415 let normalized = sel.normalize();
416 assert_eq!(
417 normalized,
418 Selector::LineNumbers(vec![LineSpec::Range(1, 5), LineSpec::Range(10, 15)])
419 );
420 }
421
422 #[test]
423 fn normalize_removes_duplicate_lines() {
424 let sel = Selector::LineNumbers(vec![
425 LineSpec::Single(5),
426 LineSpec::Single(5),
427 LineSpec::Single(5),
428 ]);
429 let normalized = sel.normalize();
430 assert_eq!(normalized, Selector::LineNumbers(vec![LineSpec::Single(5)]));
431 }
432
433 #[test]
434 fn normalize_empty_selector() {
435 let sel = Selector::LineNumbers(vec![]);
436 let normalized = sel.normalize();
437 assert_eq!(normalized, Selector::LineNumbers(vec![]));
438 }
439}