grit_pattern_matcher/pattern/
range.rs

1use super::{
2    patterns::{Matcher, PatternName},
3    resolved_pattern::ResolvedPattern,
4    state::State,
5};
6use crate::context::{ExecContext, QueryContext};
7use grit_util::{
8    error::{GritPatternError, GritResult},
9    AnalysisLogs, UtilRange,
10};
11
12#[derive(Debug, Clone)]
13pub struct Point {
14    line: u32,
15    column: Option<u32>,
16}
17
18impl Point {
19    pub fn new(line: Option<u32>, column: Option<u32>) -> GritResult<Option<Self>> {
20        if let Some(line) = line {
21            Ok(Some(Self { line, column }))
22        } else {
23            column
24                .map(|_| {
25                    Err(GritPatternError::new(
26                        "cannot have a point with a column index, but no line",
27                    ))
28                })
29                .unwrap_or(Ok(None))
30        }
31    }
32}
33
34#[derive(Debug, Clone)]
35pub struct Range {
36    pub start: Option<Point>,
37    pub end: Option<Point>,
38}
39
40impl From<UtilRange> for Range {
41    fn from(util_range: UtilRange) -> Self {
42        // TODO: there must be a smarter way
43        match util_range {
44            UtilRange::Range(range) => Self {
45                start: Some(Point {
46                    line: range.start.line,
47                    column: Some(range.start.column),
48                }),
49                end: Some(Point {
50                    line: range.end.line,
51                    column: Some(range.end.column),
52                }),
53            },
54            UtilRange::RangeWithoutByte(range_without_byte) => Self {
55                start: Some(Point {
56                    line: range_without_byte.start.line,
57                    column: Some(range_without_byte.start.column),
58                }),
59                end: Some(Point {
60                    line: range_without_byte.end.line,
61                    column: Some(range_without_byte.end.column),
62                }),
63            },
64        }
65    }
66}
67
68impl<Q: QueryContext> Matcher<Q> for Range {
69    fn execute<'a>(
70        &'a self,
71        binding: &Q::ResolvedPattern<'a>,
72        _state: &mut State<'a, Q>,
73        context: &'a Q::ExecContext<'a>,
74        _logs: &mut AnalysisLogs,
75    ) -> GritResult<bool> {
76        if let Some(range) = binding.position(context.language()) {
77            if let Some(start) = &self.start {
78                if start.line > range.start.line {
79                    return Ok(false);
80                }
81                if let Some(column) = start.column {
82                    if start.line == range.start.line && column > range.start.column {
83                        return Ok(false);
84                    }
85                }
86            }
87            if let Some(end) = &self.end {
88                if end.line < range.end.line {
89                    return Ok(false);
90                }
91                if let Some(column) = end.column {
92                    if end.line == range.end.line && column < range.end.column {
93                        return Ok(false);
94                    }
95                }
96            }
97            return Ok(true);
98        }
99        Ok(false)
100    }
101}
102
103impl PatternName for Range {
104    fn name(&self) -> &'static str {
105        "RANGE"
106    }
107}