this repo has no description
1//! Types which augment or modify the behavior of an underlying parser.
2
3use crate::error::ParseError;
4#[cfg(feature = "either")]
5use crate::label::Label;
6use crate::parse::Parser;
7use crate::storage::StorageLock;
8use crate::tag::KeyValueSep;
9use crate::tag::PathSep;
10#[cfg(feature = "either")]
11use crate::tag::Tag;
12#[cfg(feature = "convert_case")]
13pub use convert_case::Case;
14#[cfg(feature = "convert_case")]
15use convert_case::Casing as _;
16#[cfg(feature = "either")]
17use either::Either;
18#[cfg(feature = "regex")]
19use regex::Regex;
20#[cfg(feature = "regex")]
21use regex::Replacer;
22use std::hash::BuildHasher;
23use string_interner::backend::Backend as InternerBackend;
24#[cfg(feature = "either")]
25use string_interner::Symbol;
26
27// Helper macro to generate parser adapters.
28macro_rules! adapters {
29 (
30 $(
31 $( #[$($attrss:meta)*] )*
32 $struct:ident $(< $($type_var:ident: $type_bound:ident),* >)? $(($($field_ty:ty),*))? => { $adapter:expr }
33 )*
34 ) => {
35 $(
36 adapters! {
37 @single
38 $( #[$($attrss)*] )*
39 $struct $(< $($type_var: $type_bound),* >)* $(($($field_ty),*))* => { $adapter }
40 }
41 )*
42 };
43
44 (
45 @single
46 $(#[$($attrss:meta)*] )*
47 $struct:ident $(< $($type_var:ident: $type_bound:ident),* >)? $(($($field_ty:ty),* ))? => { $adapter:expr }
48 ) => {
49 $( #[$($attrss)*] )*
50 #[derive(Debug, Clone)]
51 pub struct $struct<$($($type_var: $type_bound),*,)* P: Parser>($($(pub $field_ty),*,)* pub P);
52
53 impl<$($($type_var: $type_bound),*,)* P: Parser> $struct<$($($type_var),*,)* P> {
54 /// Parse a token with the given `interner` and `separator`.
55 #[allow(clippy::redundant_closure_call)]
56 fn parse<B, H>(
57 &self,
58 storage: &mut StorageLock<'_, <P::Tag as Tag>::Label, B, H>,
59 key_value_separator: KeyValueSep,
60 path_separator: PathSep,
61 raw: &str,
62 ) -> Result<P::Tag, ParseError>
63 where
64 B: InternerBackend<Symbol = <P::Tag as Tag>::Symbol>,
65 H: BuildHasher
66 {
67 ($adapter)(self, storage, key_value_separator, path_separator, raw)
68 }
69
70 }
71
72 impl<$($($type_var: $type_bound),*,)* P: Parser> Parser for $struct<$($($type_var),*,)* P> {
73 type Tag = P::Tag;
74
75 fn parse<B, H>(
76 &self,
77 storage: &mut StorageLock<'_, <Self::Tag as Tag>::Label, B, H>,
78 key_value_separator: KeyValueSep,
79 path_separator: PathSep,
80 raw: &str,
81 ) -> Result<Self::Tag, ParseError>
82 where
83 B: InternerBackend<Symbol = <Self::Tag as Tag>::Symbol>,
84 H: BuildHasher
85 {
86 self.parse(storage, key_value_separator, path_separator, raw)
87 }
88 }
89 };
90}
91
92adapters! {
93 /// Trim whitespace from one or both sides of the tag.
94 Trim(TrimBounds) => {
95 |this: &Trim<P>, interner, kv_sep, path_sep, raw: &str| {
96 let Trim::<P>(bounds, sub_parser) = this;
97 let raw = match bounds {
98 TrimBounds::Both => raw.trim(),
99 TrimBounds::Start => raw.trim_start(),
100 TrimBounds::End => raw.trim_end(),
101 };
102 sub_parser.parse(interner, kv_sep, path_sep, raw)
103 }
104 }
105
106 /// Filter out tags longer than a maximum number of characters.
107 MaxChar(usize) => {
108 |this: &MaxChar<P>, interner, kv_sep, path_sep, raw: &str| {
109 let MaxChar::<P>(limit, sub_parser) = this;
110
111 if raw.chars().count() > *limit {
112 return Err(ParseError::TagTooManyChars);
113 }
114
115 sub_parser.parse(interner, kv_sep, path_sep, raw)
116 }
117 }
118
119 /// Filter out tags longer than a maximum number of bytes.
120 MaxBytes(usize) => {
121 |this: &MaxBytes<P>, interner, kv_sep, path_sep, raw: &str| {
122 let MaxBytes::<P>(limit, sub_parser) = this;
123
124 if raw.len() > *limit {
125 return Err(ParseError::TagTooManyBytes);
126 }
127
128 sub_parser.parse(interner, kv_sep, path_sep, raw)
129 }
130 }
131}
132
133#[cfg(feature = "convert_case")]
134adapters! {
135 /// Change the case of the tag.
136 ChangeCase(Case) => {
137 |this: &ChangeCase<P>, interner, kv_sep, path_sep, raw: &str| {
138 let ChangeCase::<P>(case, sub_parser) = this;
139 let raw = raw.to_case(*case);
140 sub_parser.parse(interner, kv_sep, path_sep, &raw)
141 }
142 }
143}
144
145#[cfg(feature = "regex")]
146adapters! {
147 /// Filter tags by matching against a regex.
148 Match(Regex) => {
149 |this: &Match<P>, interner, kv_sep, path_sep, raw: &str| {
150 let Match::<P>(regex, sub_parser) = this;
151
152 let raw = regex
153 .is_match(raw)
154 .then_some(raw)
155 .ok_or(ParseError::TagDidntMatchRegex)?;
156
157 sub_parser.parse(interner, kv_sep, path_sep, raw)
158 }
159 }
160
161 /// Replace the content of a tag according to a regex.
162 Replace<R: CloneableReplacer>(Regex, R, ReplaceCount) => {
163 |this: &Replace<R, P>, interner, kv_sep, path_sep, raw: &str| {
164 let Replace::<R, P>(regex, replacer, count, sub_parser) = this;
165
166 let raw = match count {
167 ReplaceCount::First => regex.replace(raw, replacer.clone()),
168 ReplaceCount::N(count) => regex.replacen(raw, *count, replacer.clone()),
169 ReplaceCount::All => regex.replace_all(raw, replacer.clone()),
170 };
171
172 sub_parser.parse(interner, kv_sep, path_sep, &raw)
173 }
174 }
175}
176
177/// A `regex::Replacer` that can be [`Clone`]d.
178///
179/// This is automatically implemented for any type that implements both
180/// `regex::Replacer` and [`Clone`].
181pub trait CloneableReplacer: Replacer + Clone {}
182
183impl<T: Replacer + Clone> CloneableReplacer for T {}
184
185// The `Or` adapter is implemented by hand, because making the adapter-generating
186// macro learn how to handle all of these bounds and everything isn't worth it.
187
188/// Apply one parser, and if it fails, apply the other one.
189///
190/// Note that the tokens produced by the two parsers have to support the same underlying
191/// symbol type, as they're both being backed by the same interner for storage.
192#[cfg(feature = "either")]
193#[derive(Debug)]
194pub struct Or<L, S, T1, T2, P1, P2>(pub P1, pub P2)
195where
196 L: Label,
197 S: Symbol,
198 T1: Tag<Label = L, Symbol = S>,
199 T2: Tag<Label = L, Symbol = S>,
200 P1: Parser<Tag = T1>,
201 P2: Parser<Tag = T2>;
202
203#[cfg(feature = "either")]
204impl<L, S, T1, T2, P1, P2> Or<L, S, T1, T2, P1, P2>
205where
206 L: Label,
207 S: Symbol,
208 T1: Tag<Label = L, Symbol = S>,
209 T2: Tag<Label = L, Symbol = S>,
210 P1: Parser<Tag = T1>,
211 P2: Parser<Tag = T2>,
212{
213 /// Parse a token with the given `interner` and `separator`.
214 fn parse<B, H>(
215 &self,
216 storage: &mut StorageLock<'_, L, B, H>,
217 key_value_separator: KeyValueSep,
218 path_separator: PathSep,
219 raw: &str,
220 ) -> Result<Either<T1, T2>, ParseError>
221 where
222 B: InternerBackend<Symbol = S>,
223 H: BuildHasher,
224 {
225 self.0
226 .parse(storage, key_value_separator, path_separator, raw)
227 .map(Either::Left)
228 .or_else(|err1| {
229 self.1
230 .parse(storage, key_value_separator, path_separator, raw)
231 .map(Either::Right)
232 .map_err(|err2| ParseError::FailedOr(Box::new(err1), Box::new(err2)))
233 })
234 }
235}
236
237#[cfg(feature = "either")]
238impl<L, S, T1, T2, P1, P2> Parser for Or<L, S, T1, T2, P1, P2>
239where
240 L: Label,
241 S: Symbol,
242 T1: Tag<Label = L, Symbol = S>,
243 T2: Tag<Label = L, Symbol = S>,
244 P1: Parser<Tag = T1>,
245 P2: Parser<Tag = T2>,
246{
247 type Tag = Either<T1, T2>;
248
249 fn parse<B, H>(
250 &self,
251 storage: &mut StorageLock<'_, L, B, H>,
252 key_value_separator: KeyValueSep,
253 path_separator: PathSep,
254 raw: &str,
255 ) -> Result<Self::Tag, ParseError>
256 where
257 B: InternerBackend<Symbol = S>,
258 H: BuildHasher,
259 {
260 self.parse(storage, key_value_separator, path_separator, raw)
261 }
262}
263
264/// Sets which side(s) of the raw tag should be trimmed of whitespace.
265#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
266pub enum TrimBounds {
267 /// Both sides should be trimmed.
268 Both,
269
270 /// Just the starting side should be trimmed.
271 Start,
272
273 /// Just the ending side should be trimmed.
274 End,
275}
276
277/// Sets how many replacements should be done when using the [`Replace`] adapter.
278#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
279pub enum ReplaceCount {
280 /// Replace just the first instance of the regex match.
281 First,
282
283 /// Replace the first `N` instances of the regex match.
284 N(usize),
285
286 /// Replace all instances of the regex match.
287 All,
288}