litcheck_filecheck/pattern/
matches.rs

1use crate::common::*;
2
3use super::iter::MatchIter;
4
5#[derive(Debug, Default)]
6pub struct Matches<'input>(SmallVec<[MatchResult<'input>; 1]>);
7impl<'input> Matches<'input> {
8    pub fn with_capacity(cap: usize) -> Self {
9        Self(SmallVec::with_capacity(cap))
10    }
11
12    #[inline]
13    pub fn len(&self) -> usize {
14        self.0.len()
15    }
16
17    #[inline]
18    pub fn is_empty(&self) -> bool {
19        self.0.is_empty()
20    }
21
22    #[inline]
23    pub fn as_slice(&self) -> &[MatchResult<'input>] {
24        self.0.as_slice()
25    }
26
27    /// Returns true if all of the matches are successful
28    pub fn is_ok(&self) -> bool {
29        self.0.iter().all(|mr| mr.is_ok())
30    }
31
32    /// Returns true if at least one match failed
33    pub fn has_errors(&self) -> bool {
34        !self.is_ok()
35    }
36
37    #[inline]
38    pub fn iter(&self) -> impl Iterator<Item = &MatchResult<'input>> + '_ {
39        self.0.iter()
40    }
41
42    #[inline]
43    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut MatchResult<'input>> + '_ {
44        self.0.iter_mut()
45    }
46
47    pub fn push(&mut self, result: MatchResult<'input>) {
48        use core::cmp::Ordering;
49
50        match self
51            .0
52            .binary_search_by(|probe| match (probe.info.as_ref(), result.info.as_ref()) {
53                // Always place new failed matches at the end
54                (_, None) => Ordering::Less,
55                // Failed matches always come last
56                (None, Some(_)) => Ordering::Greater,
57                (Some(a), Some(b)) => a
58                    .span
59                    .start()
60                    .cmp(&b.span.start())
61                    .then_with(|| a.span.end().cmp(&b.span.end()))
62                    .then_with(|| a.pattern_span.start().cmp(&b.pattern_span.start())),
63            }) {
64            Ok(index) | Err(index) => {
65                self.0.insert(index, result);
66            }
67        }
68    }
69
70    pub fn append(&mut self, matches: &mut Self) {
71        self.0.append(&mut matches.0);
72        self.sort();
73    }
74
75    pub fn extend<I>(&mut self, iter: I)
76    where
77        I: IntoIterator<Item = MatchResult<'input>>,
78    {
79        self.0.extend(iter);
80    }
81
82    /// Return the maximum extent of the successful matches, or None if
83    /// no matches were successful.
84    pub fn range(&self) -> Option<Range<usize>> {
85        match self.0.as_slice() {
86            [MatchResult {
87                info: Some(info),
88                ty,
89            }] if ty.is_ok() => Some(info.matched_range()),
90            [MatchResult {
91                info: Some(info),
92                ty,
93            }, .., MatchResult {
94                info: Some(info2),
95                ty: ty2,
96            }] if ty.is_ok() && ty2.is_ok() => {
97                Some(Range::new(info.span.start(), info2.span.end()))
98            }
99            matches => matches
100                .iter()
101                .find_map(|mr| mr.matched_range())
102                .map(|start_range| {
103                    matches
104                        .iter()
105                        .rev()
106                        .find_map(|mr| {
107                            mr.matched_range()
108                                .map(|r| Range::new(start_range.start, r.end))
109                        })
110                        .unwrap_or(start_range)
111                }),
112        }
113    }
114
115    fn sort(&mut self) {
116        use core::cmp::Ordering;
117
118        self.0
119            .sort_unstable_by(|a, b| match (a.info.as_ref(), b.info.as_ref()) {
120                (None, None) => Ordering::Equal,
121                (Some(_), None) => Ordering::Less,
122                (None, Some(_)) => Ordering::Greater,
123                (Some(a), Some(b)) => a
124                    .span
125                    .start()
126                    .cmp(&b.span.start())
127                    .then_with(|| a.span.end().cmp(&b.span.end()))
128                    .then_with(|| a.pattern_span.start().cmp(&b.pattern_span.start())),
129            });
130    }
131}
132impl<'input> From<MatchResult<'input>> for Matches<'input> {
133    fn from(result: MatchResult<'input>) -> Self {
134        Self(smallvec![result])
135    }
136}
137impl<'input> FromIterator<MatchResult<'input>> for Matches<'input> {
138    fn from_iter<T>(iter: T) -> Self
139    where
140        T: IntoIterator<Item = MatchResult<'input>>,
141    {
142        let mut matches = Self(SmallVec::from_iter(iter));
143        matches.sort();
144        matches
145    }
146}
147impl<'input> IntoIterator for Matches<'input> {
148    type Item = Result<Option<MatchInfo<'input>>, CheckFailedError>;
149    type IntoIter = MatchIter<'input, 1>;
150
151    #[inline]
152    fn into_iter(self) -> Self::IntoIter {
153        MatchIter::new(self.0.into_iter())
154    }
155}