1use super::Matcher;
4use crate::selector::{LineSpec, Selector};
5use crate::{Line, MatchInfo};
6
7pub struct LineMatcher {
9 ranges: Vec<(u64, u64)>,
11}
12
13impl LineMatcher {
14 pub fn from_selector(sel: &Selector) -> Self {
16 let normalized = sel.normalize();
17 let specs: &[LineSpec] = match &normalized {
18 Selector::LineNumbers(s) => s,
19 Selector::All => &[],
20 Selector::Positions(_) => {
21 panic!("LineMatcher::from_selector called with positional selector")
22 }
23 };
24 let ranges = specs
25 .iter()
26 .map(|s| match s {
27 LineSpec::Single(n) => (*n as u64, *n as u64),
28 LineSpec::Range(a, b) => (*a as u64, *b as u64),
29 })
30 .collect();
31 Self { ranges }
32 }
33}
34
35impl Matcher for LineMatcher {
36 fn match_line(&mut self, line: &Line) -> MatchInfo {
37 let hit = self
38 .ranges
39 .iter()
40 .any(|&(a, b)| line.no >= a && line.no <= b);
41 MatchInfo {
42 hit,
43 ..MatchInfo::default()
44 }
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 fn mk_line(n: u64) -> Line {
53 Line::new(n, Vec::new())
54 }
55
56 #[test]
57 fn single_line_hits_only_that_line() {
58 let sel = Selector::parse("5").unwrap();
59 let mut m = LineMatcher::from_selector(&sel);
60 assert!(!m.match_line(&mk_line(4)).hit);
61 assert!(m.match_line(&mk_line(5)).hit);
62 assert!(!m.match_line(&mk_line(6)).hit);
63 }
64
65 #[test]
66 fn range_hits_inclusive() {
67 let sel = Selector::parse("10-12").unwrap();
68 let mut m = LineMatcher::from_selector(&sel);
69 assert!(!m.match_line(&mk_line(9)).hit);
70 assert!(m.match_line(&mk_line(10)).hit);
71 assert!(m.match_line(&mk_line(11)).hit);
72 assert!(m.match_line(&mk_line(12)).hit);
73 assert!(!m.match_line(&mk_line(13)).hit);
74 }
75
76 #[test]
77 fn mixed_list_merges_ranges() {
78 let sel = Selector::parse("1,5,10-15,14").unwrap();
79 let mut m = LineMatcher::from_selector(&sel);
80 assert!(m.match_line(&mk_line(1)).hit);
81 assert!(!m.match_line(&mk_line(2)).hit);
82 assert!(m.match_line(&mk_line(5)).hit);
83 assert!(m.match_line(&mk_line(12)).hit);
84 assert!(m.match_line(&mk_line(15)).hit);
85 assert!(!m.match_line(&mk_line(16)).hit);
86 }
87}