Skip to main content

macro_rules_rt/
match_all.rs

1//! Types used in [`Rule::match_all`] results.
2
3use crate::{
4    matcher::FindAllPartMatch,
5    token_entry::{Source, TokenStringBuilder},
6    utils::is_empty,
7    Rule,
8};
9use std::{
10    borrow::Cow,
11    fmt::{Debug, Display, Formatter},
12    mem::take,
13    ops::Range,
14};
15
16#[derive(Debug)]
17pub(crate) struct MatchAllBuilder<'a> {
18    source: Source<'a>,
19    parts: Vec<RawPart>,
20    changes: Vec<RawChangedPart>,
21
22    no_match_tes_start: usize,
23    no_match_tes_end: usize,
24}
25
26impl<'a> MatchAllBuilder<'a> {
27    pub fn new(source: Source<'a>) -> Self {
28        Self {
29            source,
30            parts: Vec::new(),
31            changes: Vec::new(),
32            no_match_tes_start: 0,
33            no_match_tes_end: 0,
34        }
35    }
36    pub fn push_no_match(&mut self, tes_len: usize) {
37        self.no_match_tes_end += tes_len;
38    }
39    fn commit_no_match(&mut self) {
40        let tes_range = self
41            .source
42            .unchanged_tes_range_in(self.no_match_tes_start..self.no_match_tes_end);
43        self.push_transform(self.no_match_tes_start..tes_range.start);
44        self.push_unchanged(tes_range.clone());
45        self.push_transform(tes_range.end..self.no_match_tes_end);
46        let tes_offset = self.no_match_tes_end;
47        self.no_match_tes_start = tes_offset;
48        self.no_match_tes_end = tes_offset;
49    }
50    pub fn push_match(&mut self, m: FindAllPartMatch) {
51        self.commit_no_match();
52        let tes_start = self.no_match_tes_end;
53        let tes_end = tes_start + m.tes_len;
54        self.changes.push(RawChangedPart {
55            tes_range: tes_start..tes_end,
56            m: Some(m),
57        });
58        self.no_match_tes_start = tes_end;
59        self.no_match_tes_end = tes_end;
60    }
61    fn push_transform(&mut self, tes_range: Range<usize>) {
62        if is_empty(&tes_range) {
63            return;
64        }
65        self.changes.push(RawChangedPart { tes_range, m: None });
66    }
67    fn push_unchanged(&mut self, tes_range: Range<usize>) {
68        if is_empty(&tes_range) {
69            return;
70        }
71        self.commit_changes();
72        self.parts.push(RawPart::Unchanged { tes_range });
73    }
74    fn commit_changes(&mut self) {
75        if !self.changes.is_empty() {
76            let tes_start = self.changes[0].tes_range.start;
77            let tes_end = self.changes[self.changes.len() - 1].tes_range.end;
78            self.parts.push(RawPart::Changed(RawChanged {
79                tes_range: tes_start..tes_end,
80                parts: take(&mut self.changes),
81            }));
82        }
83    }
84    pub fn finish(mut self, rule: &'a Rule) -> MatchAll<'a> {
85        self.commit_no_match();
86        self.commit_changes();
87        MatchAll {
88            source: self.source,
89            rule,
90            parts: self.parts,
91        }
92    }
93}
94
95/// Result of [`Rule::match_all`]
96pub struct MatchAll<'a> {
97    source: Source<'a>,
98    rule: &'a Rule,
99    parts: Vec<RawPart>,
100}
101
102impl<'a> MatchAll<'a> {
103    /// Obtain the pre-conversion string.
104    pub fn input(&self) -> &str {
105        self.source.as_str()
106    }
107
108    /// Obtain the converted string.
109    pub fn output(&self) -> String {
110        let mut b = TokenStringBuilder::new(&self.source);
111        for p in self.iter() {
112            p.output_to(&mut b);
113        }
114        b.s
115    }
116    pub fn iter(&self) -> impl Iterator<Item = MatchAllPart> {
117        self.parts
118            .iter()
119            .map(|part| match part {
120                RawPart::Unchanged { tes_range } => MatchAllPart::Unchanged(Unchanged {
121                    ma: self,
122                    tes_range: tes_range.clone(),
123                }),
124                RawPart::Changed(p) => MatchAllPart::Changed(Changed { ma: self, p }),
125            })
126            .collect::<Vec<_>>()
127            .into_iter()
128    }
129}
130impl Debug for MatchAll<'_> {
131    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
132        f.debug_list().entries(self.iter()).finish()
133    }
134}
135
136#[derive(Debug)]
137enum RawPart {
138    Unchanged { tes_range: Range<usize> },
139    Changed(RawChanged),
140}
141
142#[derive(Debug)]
143struct RawChanged {
144    tes_range: Range<usize>,
145    parts: Vec<RawChangedPart>,
146}
147#[derive(Debug)]
148struct RawChangedPart {
149    tes_range: Range<usize>,
150    m: Option<FindAllPartMatch>,
151}
152
153/// A part of the result of [`Rule::match_all`].
154#[derive(Debug)]
155pub enum MatchAllPart<'a> {
156    Unchanged(Unchanged<'a>),
157    Changed(Changed<'a>),
158}
159
160impl<'a> MatchAllPart<'a> {
161    /// Obtain the pre-conversion string.
162    pub fn input(&self) -> &str {
163        match self {
164            MatchAllPart::Unchanged(u) => u.as_str(),
165            MatchAllPart::Changed(c) => c.input(),
166        }
167    }
168
169    /// Obtain the converted string.
170    pub fn output(&self) -> Cow<str> {
171        match self {
172            MatchAllPart::Unchanged(u) => Cow::Borrowed(u.as_str()),
173            MatchAllPart::Changed(c) => Cow::Owned(c.output()),
174        }
175    }
176    fn output_to(&self, sb: &mut TokenStringBuilder) {
177        match self {
178            MatchAllPart::Unchanged(u) => sb.push_str(u.as_str()),
179            MatchAllPart::Changed(c) => c.output_to(sb),
180        }
181    }
182}
183
184/// Range over which no conversion was performed.
185pub struct Unchanged<'a> {
186    ma: &'a MatchAll<'a>,
187    tes_range: Range<usize>,
188}
189impl<'a> Unchanged<'a> {
190    pub fn as_str(&self) -> &str {
191        self.ma.source.get_source(self.tes_range.clone())
192    }
193}
194impl Display for Unchanged<'_> {
195    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
196        write!(f, "{}", self.as_str())
197    }
198}
199impl Debug for Unchanged<'_> {
200    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
201        write!(f, "{}", self.as_str())
202    }
203}
204
205/// Range where conversion was performed.
206pub struct Changed<'a> {
207    ma: &'a MatchAll<'a>,
208    p: &'a RawChanged,
209}
210
211impl<'a> Changed<'a> {
212    /// Obtain the pre-conversion string corresponding to this range.
213    pub fn input(&self) -> &str {
214        self.ma.source.get_source(self.p.tes_range.clone())
215    }
216
217    /// Obtain the converted string corresponding to this range.
218    pub fn output(&self) -> String {
219        let mut b = TokenStringBuilder::new(&self.ma.source);
220        self.output_to(&mut b);
221        b.s
222    }
223    fn output_to(&self, b: &mut TokenStringBuilder) {
224        for p in &self.p.parts {
225            if let Some(m) = &p.m {
226                m.apply_string(self.ma.rule, b);
227            } else {
228                b.push_tes(p.tes_range.clone());
229            }
230        }
231    }
232
233    pub fn iter(&self) -> impl Iterator<Item = ChangedPart> {
234        let ma = &self.ma;
235        self.p.parts.iter().map(|p| {
236            if let Some(m) = &p.m {
237                ChangedPart::Match(Match { ma, m })
238            } else {
239                ChangedPart::Transform(Transform {
240                    ma,
241                    tes_range: p.tes_range.clone(),
242                })
243            }
244        })
245    }
246}
247impl Debug for Changed<'_> {
248    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
249        f.debug_struct("Changed")
250            .field("input", &self.input())
251            .field("output", &self.output())
252            .field("iter", &self.iter().collect::<Vec<_>>())
253            .finish()
254    }
255}
256
257/// Range where conversion was performed for a specific reason.
258#[derive(Debug)]
259pub enum ChangedPart<'a> {
260    Transform(Transform<'a>),
261    Match(Match<'a>),
262}
263
264/// Range where conversion was performed when creating `TokenStream` from a string, and did not match the search criteria.
265pub struct Transform<'a> {
266    ma: &'a MatchAll<'a>,
267    tes_range: Range<usize>,
268}
269
270impl<'a> Transform<'a> {
271    #[allow(clippy::inherent_to_string)]
272    pub fn to_string(&self) -> String {
273        let mut b = TokenStringBuilder::new(&self.ma.source);
274        b.push_tes(self.tes_range.clone());
275        b.s
276    }
277}
278impl Debug for Transform<'_> {
279    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
280        f.write_str(&self.to_string())
281    }
282}
283
284/// Range where the search criteria were met, and replacement was performed.
285pub struct Match<'a> {
286    ma: &'a MatchAll<'a>,
287    m: &'a FindAllPartMatch,
288}
289
290impl<'a> Match<'a> {
291    #[allow(clippy::inherent_to_string)]
292    pub fn to_string(&self) -> String {
293        let mut b = TokenStringBuilder::new(&self.ma.source);
294        self.m.apply_string(self.ma.rule, &mut b);
295        b.s
296    }
297}
298impl Debug for Match<'_> {
299    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
300        f.write_str(&self.to_string())
301    }
302}