Skip to main content

sel/matcher/
position.rs

1//! Positional matcher (line:column).
2
3use super::Matcher;
4use crate::selector::{Position, Selector};
5use crate::{Line, MatchInfo};
6
7pub struct PositionMatcher {
8    /// Positions sorted by `(line, col)`.
9    positions: Vec<Position>,
10    /// Index of next unconsumed position (positions with line < current are skipped).
11    cursor: usize,
12}
13
14impl PositionMatcher {
15    /// Panics if `sel` is not `Selector::Positions`.
16    pub fn from_selector(sel: &Selector) -> Self {
17        let positions = match sel.normalize() {
18            Selector::Positions(mut p) => {
19                p.sort();
20                p.dedup();
21                p
22            }
23            _ => panic!("PositionMatcher::from_selector needs Selector::Positions"),
24        };
25        Self {
26            positions,
27            cursor: 0,
28        }
29    }
30}
31
32impl Matcher for PositionMatcher {
33    fn match_line(&mut self, line: &Line) -> MatchInfo {
34        // Advance cursor past any positions for earlier lines.
35        while self.cursor < self.positions.len()
36            && (self.positions[self.cursor].line as u64) < line.no
37        {
38            self.cursor += 1;
39        }
40        // First position on this line (if any) becomes the target column.
41        if let Some(p) = self.positions.get(self.cursor)
42            && p.line as u64 == line.no
43        {
44            return MatchInfo {
45                hit: true,
46                spans: Vec::new(),
47                col: Some(p.column),
48            };
49        }
50        MatchInfo::default()
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    fn mk_line(n: u64) -> Line {
59        Line::new(n, Vec::new())
60    }
61
62    #[test]
63    fn single_position_hits_correct_line_with_col() {
64        let sel = Selector::parse("23:260").unwrap();
65        let mut m = PositionMatcher::from_selector(&sel);
66        assert!(!m.match_line(&mk_line(22)).hit);
67        let info = m.match_line(&mk_line(23));
68        assert!(info.hit);
69        assert_eq!(info.col, Some(260));
70        assert!(!m.match_line(&mk_line(24)).hit);
71    }
72
73    #[test]
74    fn multiple_positions_hit_in_order() {
75        let sel = Selector::parse("5:10,5:20,9:3").unwrap();
76        let mut m = PositionMatcher::from_selector(&sel);
77        let h5 = m.match_line(&mk_line(5));
78        assert!(h5.hit);
79        assert_eq!(h5.col, Some(10));
80        let h9 = m.match_line(&mk_line(9));
81        assert!(h9.hit);
82        assert_eq!(h9.col, Some(3));
83    }
84}