Skip to main content

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(always)]
38    pub fn into_results(self) -> SmallVec<[MatchResult<'input>; 1]> {
39        self.0
40    }
41
42    #[inline]
43    pub fn iter(&self) -> impl Iterator<Item = &MatchResult<'input>> + '_ {
44        self.0.iter()
45    }
46
47    #[inline]
48    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut MatchResult<'input>> + '_ {
49        self.0.iter_mut()
50    }
51
52    pub fn push(&mut self, result: MatchResult<'input>) {
53        use core::cmp::Ordering;
54
55        match self
56            .0
57            .binary_search_by(|probe| match (probe.info.as_ref(), result.info.as_ref()) {
58                // Always place new failed matches at the end
59                (_, None) => Ordering::Less,
60                // Failed matches always come last
61                (None, Some(_)) => Ordering::Greater,
62                (Some(a), Some(b)) => a
63                    .span
64                    .start()
65                    .cmp(&b.span.start())
66                    .then_with(|| a.span.end().cmp(&b.span.end()))
67                    .then_with(|| a.pattern_span.start().cmp(&b.pattern_span.start())),
68            }) {
69            Ok(index) | Err(index) => {
70                self.0.insert(index, result);
71            }
72        }
73    }
74
75    pub fn append(&mut self, matches: &mut Self) {
76        self.0.append(&mut matches.0);
77        self.sort();
78    }
79
80    pub fn extend<I>(&mut self, iter: I)
81    where
82        I: IntoIterator<Item = MatchResult<'input>>,
83    {
84        self.0.extend(iter);
85    }
86
87    /// Return the maximum extent of the successful matches, or None if
88    /// no matches were successful.
89    pub fn range(&self) -> Option<Range<usize>> {
90        match self.0.as_slice() {
91            [
92                MatchResult {
93                    info: Some(info),
94                    ty,
95                },
96            ] if ty.is_ok() => Some(info.matched_range()),
97            [
98                MatchResult {
99                    info: Some(info),
100                    ty,
101                },
102                ..,
103                MatchResult {
104                    info: Some(info2),
105                    ty: ty2,
106                },
107            ] if ty.is_ok() && ty2.is_ok() => Some(Range::new(
108                info.span.start().to_usize(),
109                info2.span.end().to_usize(),
110            )),
111            matches => matches
112                .iter()
113                .find_map(|mr| mr.matched_range())
114                .map(|start_range| {
115                    matches
116                        .iter()
117                        .rev()
118                        .find_map(|mr| {
119                            mr.matched_range()
120                                .map(|r| Range::new(start_range.start, r.end))
121                        })
122                        .unwrap_or(start_range)
123                }),
124        }
125    }
126
127    fn sort(&mut self) {
128        use core::cmp::Ordering;
129
130        self.0
131            .sort_unstable_by(|a, b| match (a.info.as_ref(), b.info.as_ref()) {
132                (None, None) => Ordering::Equal,
133                (Some(_), None) => Ordering::Less,
134                (None, Some(_)) => Ordering::Greater,
135                (Some(a), Some(b)) => a
136                    .span
137                    .start()
138                    .cmp(&b.span.start())
139                    .then_with(|| a.span.end().cmp(&b.span.end()))
140                    .then_with(|| a.pattern_span.start().cmp(&b.pattern_span.start())),
141            });
142    }
143}
144impl<'input> From<MatchResult<'input>> for Matches<'input> {
145    fn from(result: MatchResult<'input>) -> Self {
146        Self(smallvec![result])
147    }
148}
149impl<'input> FromIterator<MatchResult<'input>> for Matches<'input> {
150    fn from_iter<T>(iter: T) -> Self
151    where
152        T: IntoIterator<Item = MatchResult<'input>>,
153    {
154        let mut matches = Self(SmallVec::from_iter(iter));
155        matches.sort();
156        matches
157    }
158}
159impl<'input> IntoIterator for Matches<'input> {
160    type Item = Result<Option<MatchInfo<'input>>, CheckFailedError>;
161    type IntoIter = MatchIter<'input, 1>;
162
163    #[inline]
164    fn into_iter(self) -> Self::IntoIter {
165        MatchIter::new(self.0.into_iter())
166    }
167}