join_impl/chain/group/
group_determiner.rs1use crate::parse::utils::is_valid_stream;
6use proc_macro2::{TokenStream, TokenTree};
7use syn::parse::{Parse, ParseStream};
8
9use super::Combinator;
10
11pub type CheckStreamFn = fn(ParseStream) -> bool;
12
13#[derive(Clone, Copy)]
14union CheckStreamFnPointer {
15 f: CheckStreamFn,
16 raw: *const (),
17}
18
19unsafe impl std::marker::Send for CheckStreamFnPointer {}
20
21unsafe impl std::marker::Sync for CheckStreamFnPointer {}
22
23#[derive(Clone)]
27pub struct GroupDeterminer {
28 combinator: Option<Combinator>,
29 check_input_fn: CheckStreamFnPointer,
30 validate_parsed: bool,
31 length: u8,
32}
33
34#[macro_export]
38macro_rules! define_group_determiners {
39 ($($combinator: ident => $($token: expr),+ => $length: expr),+) => {{
40 [
41 $crate::define_determiner_with_no_group!(Token![,] => 0),
42 $(
43 $crate::define_group_determiner!(
44 $crate::chain::group::Combinator::$combinator => $($token),+ => $length
45 )
46 ),*,
47 $crate::chain::group::GroupDeterminer::new_const(
48 None,
49 $crate::handler::Handler::peek_handler as *const (),
50 true,
51 0
52 )
53 ]
54 }};
55}
56
57#[macro_export]
61macro_rules! define_determiner_with_no_group {
62 ($($token: expr),+ => $length: expr) => {{
63 let check_tokens = $crate::define_tokens_checker!($($token),+);
64 $crate::chain::group::GroupDeterminer::new_const(
65 None,
66 check_tokens as *const (),
67 true,
68 $length
69 )
70 }}
71}
72
73#[macro_export]
77macro_rules! define_tokens_checker {
78 ($token1: expr, $token2:expr, $token3:expr) => {{
79 fn check_tokens(input: ::syn::parse::ParseStream<'_>) -> bool {
80 input.peek($token1) && input.peek2($token2) && input.peek3($token3)
81 }
82 check_tokens
83 }};
84 ($token1: expr, $token2:expr) => {{
85 fn check_tokens(input: ::syn::parse::ParseStream<'_>) -> bool {
86 input.peek($token1) && input.peek2($token2)
87 }
88 check_tokens
89 }};
90 ($token: expr) => {{
91 fn check_tokens(input: ::syn::parse::ParseStream<'_>) -> bool { input.peek($token) }
92 check_tokens
93 }};
94 ($($token: expr),+) => {{
95 fn check_tokens(input: ::syn::parse::ParseStream<'_>) -> bool {
96 let input = input.fork();
97 $(
98 input.peek($token) && $crate::parse::utils::skip(&input)
99 )&&+
100 }
101 check_tokens
102 }};
103}
104
105#[macro_export]
118macro_rules! define_group_determiner {
119 ($combinator: expr => $($tokens: expr),+ => $length: expr => $validate_parsed: expr) => {{
120 let check_tokens = $crate::define_tokens_checker!($($tokens),*);
121 $crate::chain::group::GroupDeterminer::new_const(
122 Some($combinator),
123 check_tokens as *const (),
124 $validate_parsed,
125 $length
126 )
127 }};
128 ($combinator: expr => $($tokens: expr),+ => $length: expr) => {
129 $crate::define_group_determiner!(
130 $combinator => $($tokens),+ => $length => true
131 )
132 };
133}
134
135impl GroupDeterminer {
136 pub const fn new_const(
159 combinator: Option<Combinator>,
160 check_input_fn: *const (),
161 validate_parsed: bool,
162 length: u8,
163 ) -> Self {
164 Self {
165 combinator,
166 check_input_fn: CheckStreamFnPointer {
167 raw: check_input_fn,
168 },
169 validate_parsed,
170 length,
171 }
172 }
173
174 pub fn new(
197 combinator: impl Into<Option<Combinator>>,
198 check_input_fn: CheckStreamFn,
199 validate_parsed: bool,
200 length: u8,
201 ) -> Self {
202 Self {
203 combinator: combinator.into(),
204 check_input_fn: CheckStreamFnPointer { f: check_input_fn },
205 validate_parsed,
206 length,
207 }
208 }
209
210 pub fn combinator(&self) -> Option<Combinator> {
214 self.combinator
215 }
216
217 pub fn check_input(&self, input: ParseStream<'_>) -> bool {
221 (unsafe { self.check_input_fn.f })(input)
222 }
223
224 pub fn check_parsed<T: Parse>(&self, input: TokenStream) -> bool {
230 !self.validate_parsed || is_valid_stream::<T>(input)
231 }
232
233 pub fn erase_input<'b>(&self, input: ParseStream<'b>) -> syn::Result<ParseStream<'b>> {
237 for _ in 0..self.len() {
238 input.parse::<TokenTree>()?;
239 }
240 Ok(input)
241 }
242
243 pub fn len(&self) -> u8 {
247 self.length
248 }
249
250 pub fn is_empty(&self) -> bool {
254 self.len() == 0
255 }
256}
257
258#[cfg(test)]
259mod tests {
260 use super::{super::*, *};
261
262 #[test]
263 fn it_creates_comma_determiner() {
264 fn check_comma(input: ParseStream) -> bool {
265 input.peek(::syn::Token![,])
266 }
267
268 let first_comma_determiner = GroupDeterminer::new_const(
269 None, check_comma as *const (),
271 false,
272 1,
273 );
274
275 assert_eq!(first_comma_determiner.combinator(), None);
276 assert!(first_comma_determiner.check_parsed::<::syn::Expr>(::quote::quote! { , }));
277 assert_eq!(first_comma_determiner.len(), 1);
278 }
279
280 #[test]
281 fn it_creates_then_determiner() {
282 fn check_then(input: ParseStream) -> bool {
283 input.peek(::syn::Token![=>])
284 }
285
286 let then_determiner = GroupDeterminer::new(Combinator::Then, check_then, true, 2);
287
288 assert_eq!(then_determiner.combinator(), Some(Combinator::Then));
289 assert!(then_determiner.check_parsed::<::syn::Expr>(::quote::quote! { 23 }));
290 assert_eq!(then_determiner.len(), 2);
291 }
292}