syn/
custom_punctuation.rs

1/// Define a type that supports parsing and printing a multi-character symbol
2/// as if it were a punctuation token.
3///
4/// # Usage
5///
6/// ```
7/// syn::custom_punctuation!(LeftRightArrow, <=>);
8/// ```
9///
10/// The generated syntax tree node supports the following operations just like
11/// any built-in punctuation token.
12///
13/// - [Peeking] — `input.peek(LeftRightArrow)`
14///
15/// - [Parsing] — `input.parse::<LeftRightArrow>()?`
16///
17/// - [Printing] — `quote!( ... #lrarrow ... )`
18///
19/// - Construction from a [`Span`] — `let lrarrow = LeftRightArrow(sp)`
20///
21/// - Construction from multiple [`Span`] — `let lrarrow = LeftRightArrow([sp, sp, sp])`
22///
23/// - Field access to its spans — `let spans = lrarrow.spans`
24///
25/// [Peeking]: crate::parse::ParseBuffer::peek
26/// [Parsing]: crate::parse::ParseBuffer::parse
27/// [Printing]: quote::ToTokens
28/// [`Span`]: proc_macro2::Span
29///
30/// # Example
31///
32/// ```
33/// use proc_macro2::{TokenStream, TokenTree};
34/// use std::iter;
35/// use syn::parse::{Parse, ParseStream, Peek, Result};
36/// use syn::punctuated::Punctuated;
37/// use syn::Expr;
38///
39/// syn::custom_punctuation!(PathSeparator, </>);
40///
41/// // expr </> expr </> expr ...
42/// struct PathSegments {
43///     segments: Punctuated<Expr, PathSeparator>,
44/// }
45///
46/// impl Parse for PathSegments {
47///     fn parse(input: ParseStream) -> Result<Self> {
48///         let mut segments = Punctuated::new();
49///
50///         let first = parse_until(input, PathSeparator)?;
51///         segments.push_value(syn::parse2(first)?);
52///
53///         while input.peek(PathSeparator) {
54///             segments.push_punct(input.parse()?);
55///
56///             let next = parse_until(input, PathSeparator)?;
57///             segments.push_value(syn::parse2(next)?);
58///         }
59///
60///         Ok(PathSegments { segments })
61///     }
62/// }
63///
64/// fn parse_until<E: Peek>(input: ParseStream, end: E) -> Result<TokenStream> {
65///     let mut tokens = TokenStream::new();
66///     while !input.is_empty() && !input.peek(end) {
67///         let next: TokenTree = input.parse()?;
68///         tokens.extend(iter::once(next));
69///     }
70///     Ok(tokens)
71/// }
72///
73/// fn main() {
74///     let input = r#" a::b </> c::d::e "#;
75///     let _: PathSegments = syn::parse_str(input).unwrap();
76/// }
77/// ```
78#[macro_export]
79macro_rules! custom_punctuation {
80    ($ident:ident, $($tt:tt)+) => {
81        pub struct $ident {
82            #[allow(dead_code)]
83            pub spans: $crate::custom_punctuation_repr!($($tt)+),
84        }
85
86        #[doc(hidden)]
87        #[allow(dead_code, non_snake_case)]
88        pub fn $ident<__S: $crate::__private::IntoSpans<$crate::custom_punctuation_repr!($($tt)+)>>(
89            spans: __S,
90        ) -> $ident {
91            let _validate_len = 0 $(+ $crate::custom_punctuation_len!(strict, $tt))*;
92            $ident {
93                spans: $crate::__private::IntoSpans::into_spans(spans)
94            }
95        }
96
97        const _: () = {
98            impl $crate::__private::Default for $ident {
99                fn default() -> Self {
100                    $ident($crate::__private::Span::call_site())
101                }
102            }
103
104            $crate::impl_parse_for_custom_punctuation!($ident, $($tt)+);
105            $crate::impl_to_tokens_for_custom_punctuation!($ident, $($tt)+);
106            $crate::impl_clone_for_custom_punctuation!($ident, $($tt)+);
107            $crate::impl_extra_traits_for_custom_punctuation!($ident, $($tt)+);
108        };
109    };
110}
111
112// Not public API.
113#[cfg(feature = "parsing")]
114#[doc(hidden)]
115#[macro_export]
116macro_rules! impl_parse_for_custom_punctuation {
117    ($ident:ident, $($tt:tt)+) => {
118        impl $crate::__private::CustomToken for $ident {
119            fn peek(cursor: $crate::buffer::Cursor) -> $crate::__private::bool {
120                $crate::__private::peek_punct(cursor, $crate::stringify_punct!($($tt)+))
121            }
122
123            fn display() -> &'static $crate::__private::str {
124                $crate::__private::concat!("`", $crate::stringify_punct!($($tt)+), "`")
125            }
126        }
127
128        impl $crate::parse::Parse for $ident {
129            fn parse(input: $crate::parse::ParseStream) -> $crate::parse::Result<$ident> {
130                let spans: $crate::custom_punctuation_repr!($($tt)+) =
131                    $crate::__private::parse_punct(input, $crate::stringify_punct!($($tt)+))?;
132                Ok($ident(spans))
133            }
134        }
135    };
136}
137
138// Not public API.
139#[cfg(not(feature = "parsing"))]
140#[doc(hidden)]
141#[macro_export]
142macro_rules! impl_parse_for_custom_punctuation {
143    ($ident:ident, $($tt:tt)+) => {};
144}
145
146// Not public API.
147#[cfg(feature = "printing")]
148#[doc(hidden)]
149#[macro_export]
150macro_rules! impl_to_tokens_for_custom_punctuation {
151    ($ident:ident, $($tt:tt)+) => {
152        impl $crate::__private::ToTokens for $ident {
153            fn to_tokens(&self, tokens: &mut $crate::__private::TokenStream2) {
154                $crate::__private::print_punct($crate::stringify_punct!($($tt)+), &self.spans, tokens)
155            }
156        }
157    };
158}
159
160// Not public API.
161#[cfg(not(feature = "printing"))]
162#[doc(hidden)]
163#[macro_export]
164macro_rules! impl_to_tokens_for_custom_punctuation {
165    ($ident:ident, $($tt:tt)+) => {};
166}
167
168// Not public API.
169#[cfg(feature = "clone-impls")]
170#[doc(hidden)]
171#[macro_export]
172macro_rules! impl_clone_for_custom_punctuation {
173    ($ident:ident, $($tt:tt)+) => {
174        impl $crate::__private::Copy for $ident {}
175
176        #[allow(clippy::expl_impl_clone_on_copy)]
177        impl $crate::__private::Clone for $ident {
178            fn clone(&self) -> Self {
179                *self
180            }
181        }
182    };
183}
184
185// Not public API.
186#[cfg(not(feature = "clone-impls"))]
187#[doc(hidden)]
188#[macro_export]
189macro_rules! impl_clone_for_custom_punctuation {
190    ($ident:ident, $($tt:tt)+) => {};
191}
192
193// Not public API.
194#[cfg(feature = "extra-traits")]
195#[doc(hidden)]
196#[macro_export]
197macro_rules! impl_extra_traits_for_custom_punctuation {
198    ($ident:ident, $($tt:tt)+) => {
199        impl $crate::__private::Debug for $ident {
200            fn fmt(&self, f: &mut $crate::__private::Formatter) -> $crate::__private::FmtResult {
201                $crate::__private::Formatter::write_str(f, $crate::__private::stringify!($ident))
202            }
203        }
204
205        impl $crate::__private::Eq for $ident {}
206
207        impl $crate::__private::PartialEq for $ident {
208            fn eq(&self, _other: &Self) -> $crate::__private::bool {
209                true
210            }
211        }
212
213        impl $crate::__private::Hash for $ident {
214            fn hash<__H: $crate::__private::Hasher>(&self, _state: &mut __H) {}
215        }
216    };
217}
218
219// Not public API.
220#[cfg(not(feature = "extra-traits"))]
221#[doc(hidden)]
222#[macro_export]
223macro_rules! impl_extra_traits_for_custom_punctuation {
224    ($ident:ident, $($tt:tt)+) => {};
225}
226
227// Not public API.
228#[doc(hidden)]
229#[macro_export]
230macro_rules! custom_punctuation_repr {
231    ($($tt:tt)+) => {
232        [$crate::__private::Span; 0 $(+ $crate::custom_punctuation_len!(lenient, $tt))+]
233    };
234}
235
236// Not public API.
237#[doc(hidden)]
238#[macro_export]
239#[rustfmt::skip]
240macro_rules! custom_punctuation_len {
241    ($mode:ident, &)     => { 1 };
242    ($mode:ident, &&)    => { 2 };
243    ($mode:ident, &=)    => { 2 };
244    ($mode:ident, @)     => { 1 };
245    ($mode:ident, ^)     => { 1 };
246    ($mode:ident, ^=)    => { 2 };
247    ($mode:ident, :)     => { 1 };
248    ($mode:ident, ,)     => { 1 };
249    ($mode:ident, $)     => { 1 };
250    ($mode:ident, .)     => { 1 };
251    ($mode:ident, ..)    => { 2 };
252    ($mode:ident, ...)   => { 3 };
253    ($mode:ident, ..=)   => { 3 };
254    ($mode:ident, =)     => { 1 };
255    ($mode:ident, ==)    => { 2 };
256    ($mode:ident, =>)    => { 2 };
257    ($mode:ident, >=)    => { 2 };
258    ($mode:ident, >)     => { 1 };
259    ($mode:ident, <-)    => { 2 };
260    ($mode:ident, <=)    => { 2 };
261    ($mode:ident, <)     => { 1 };
262    ($mode:ident, -)     => { 1 };
263    ($mode:ident, -=)    => { 2 };
264    ($mode:ident, !=)    => { 2 };
265    ($mode:ident, !)     => { 1 };
266    ($mode:ident, |)     => { 1 };
267    ($mode:ident, |=)    => { 2 };
268    ($mode:ident, ||)    => { 2 };
269    ($mode:ident, ::)    => { 2 };
270    ($mode:ident, %)     => { 1 };
271    ($mode:ident, %=)    => { 2 };
272    ($mode:ident, +)     => { 1 };
273    ($mode:ident, +=)    => { 2 };
274    ($mode:ident, #)     => { 1 };
275    ($mode:ident, ?)     => { 1 };
276    ($mode:ident, ->)    => { 2 };
277    ($mode:ident, ;)     => { 1 };
278    ($mode:ident, <<)    => { 2 };
279    ($mode:ident, <<=)   => { 3 };
280    ($mode:ident, >>)    => { 2 };
281    ($mode:ident, >>=)   => { 3 };
282    ($mode:ident, /)     => { 1 };
283    ($mode:ident, /=)    => { 2 };
284    ($mode:ident, *)     => { 1 };
285    ($mode:ident, *=)    => { 2 };
286    ($mode:ident, ~)     => { 1 };
287    (lenient, $tt:tt)    => { 0 };
288    (strict, $tt:tt)     => {{ $crate::custom_punctuation_unexpected!($tt); 0 }};
289}
290
291// Not public API.
292#[doc(hidden)]
293#[macro_export]
294macro_rules! custom_punctuation_unexpected {
295    () => {};
296}
297
298// Not public API.
299#[doc(hidden)]
300#[macro_export]
301macro_rules! stringify_punct {
302    ($($tt:tt)+) => {
303        $crate::__private::concat!($($crate::__private::stringify!($tt)),+)
304    };
305}