macro_rules_rt/
lib.rs

1#![warn(clippy::redundant_pub_crate)]
2
3use match_all::MatchAll;
4use matcher::MatchTokensBuilder;
5use proc_macro2::TokenStream;
6use syn::Result;
7use token_entry::{FindAllStringBuilder, ParseStreamEx, Source, TokenStringBuilder};
8
9pub use matcher::Matcher;
10pub use transcriber::Transcriber;
11
12#[macro_use]
13mod utils;
14
15pub mod match_all;
16mod matcher;
17mod text;
18mod token_entry;
19mod transcriber;
20
21/// Pair [`Matcher`] and [`Transcriber`].
22#[derive(Clone, Debug)]
23pub struct Rule {
24    from: Matcher,
25    to: Transcriber,
26    nest: bool,
27}
28
29impl Rule {
30    /// Create a new `Rule` from `Matcher` and `Transcriber`.
31    ///
32    /// Returns an error if the meta-variables of `Matcher` and `Transcriber` do not match.
33    pub fn new(from: Matcher, mut to: Transcriber) -> Result<Self> {
34        to.attach(&from.0)?;
35        Ok(Rule {
36            from,
37            to,
38            nest: false,
39        })
40    }
41    /// Specifies whether to apply replacements to metavariable matches. (default is false.)
42    ///
43    /// If false, only the outermost matched range is replaced.
44    ///
45    /// If true, further substitutions are made for the range matched by meta-variables such as `$e:expr`.
46    ///
47    /// ```rust
48    /// use macro_rules_rt::Rule;
49    ///
50    /// let from = "a + $e:expr".parse()?;
51    /// let to   = "b + $e".parse()?;
52    /// let input = "a + a + x";
53    /// let rule = Rule::new(from, to)?;
54    /// let r_nest_no = rule.clone().replace_all(input)?;
55    /// let r_nest_yes = rule.nest(true).replace_all(input)?;
56    /// assert_eq!(r_nest_no,  "b + a + x");
57    /// assert_eq!(r_nest_yes, "b + b + x");
58    /// # Ok::<(), syn::Error>(())
59    /// ```
60    pub fn nest(self, yes: bool) -> Self {
61        Self { nest: yes, ..self }
62    }
63
64    /// Replaces all non-overlapping matches in input with the provided transcriber.
65    ///
66    /// Unlike creating `TokenStream` from `str` and then calling [`Rule::replace_all`],
67    /// the original string is preserved as much as possible.
68    pub fn replace_all(&self, input: &str) -> Result<String> {
69        let (source, input) = Source::from_str(input)?;
70        let mut b = TokenStringBuilder::new(&source);
71        self.from
72            .find_all(input, 0)
73            .apply_string(self, &mut FindAllStringBuilder::new(&mut b, 0));
74        Ok(b.s)
75    }
76
77    /// Replaces all non-overlapping matches in `input` with the provided transcriber.
78    pub fn replace_all_tokens(&self, input: TokenStream) -> TokenStream {
79        self.from
80            .find_all(input.clone(), 0)
81            .apply_tokens(&mut 0, input, self)
82    }
83
84    /// Replaces all non-overlapping matches in input with the provided transcriber, and returns detailed information.
85    pub fn match_all<'a>(&'a self, input: &'a str) -> Result<MatchAll<'a>> {
86        let (source, input) = Source::from_str(input)?;
87        Ok(self.from.match_all(source, input, self))
88    }
89
90    /// If the entire `input` matches the entire `from`, do the conversion. Otherwise, return an error.
91    pub fn apply(&self, input: &str) -> Result<String> {
92        let (source, input) = Source::from_str(input)?;
93        ParseStreamEx::parse_from_tokens(input, 0, |input: &mut ParseStreamEx| {
94            let m = self.from.try_match(input)?;
95            let mut b = TokenStringBuilder::new(&source);
96            self.to.apply_string(&m, self, usize::MAX, &mut b);
97            Ok(b.s)
98        })
99    }
100
101    /// If the entire `input` matches the entire `from`, do the conversion. Otherwise, return an error.
102    pub fn apply_tokens(&self, input: TokenStream) -> Result<TokenStream> {
103        ParseStreamEx::parse_from_tokens(input, 0, |input: &mut ParseStreamEx| {
104            self.apply_tokens_parser(input)
105        })
106    }
107    fn apply_tokens_parser(&self, input: &mut ParseStreamEx) -> Result<TokenStream> {
108        let m = self.from.try_match(input)?;
109        let mut tokens = TokenStream::new();
110        let mut b = MatchTokensBuilder {
111            tokens: &mut tokens,
112            rule: self,
113            tes_len: usize::MAX,
114        };
115        self.to.apply_tokens_to(&m, &mut b);
116        Ok(tokens)
117    }
118}