Skip to main content

syn_serde/
token_stream.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3use alloc::{
4    borrow::ToOwned as _,
5    format,
6    string::{String, ToString as _},
7    vec::Vec,
8};
9use core::fmt::{self, Write as _};
10
11use super::*;
12
13ast_struct! {
14    /// An adapter for [`struct@proc_macro2::TokenStream`].
15    #[derive(Clone, Default)]
16    #[serde(transparent)]
17    pub struct TokenStream {
18        inner: Vec<TokenTree>,
19    }
20}
21
22impl TokenStream {
23    fn _new(inner: Vec<TokenTree>) -> Self {
24        Self { inner }
25    }
26}
27
28ast_enum! {
29    /// An adapter for [`enum@proc_macro2::TokenTree`].
30    #[derive(Clone)]
31    pub enum TokenTree {
32        Group(Group),
33        Ident(Ident),
34        Punct(Punct),
35        #[serde(rename = "lit")]
36        Literal(Literal),
37    }
38}
39
40ast_struct! {
41    /// An adapter for [`struct@proc_macro2::Group`].
42    #[derive(Clone)]
43    pub struct Group {
44        delimiter: Delimiter,
45        stream: TokenStream,
46    }
47}
48
49ast_enum! {
50    /// An adapter for [`enum@proc_macro2::Delimiter`].
51    #[derive(Clone, Copy)]
52    pub enum Delimiter {
53        Parenthesis,
54        Brace,
55        Bracket,
56        None,
57    }
58}
59
60ast_struct! {
61    /// An adapter for [`struct@proc_macro2::Punct`].
62    #[derive(Clone, Copy)]
63    pub struct Punct {
64        op: char,
65        spacing: Spacing,
66    }
67}
68
69ast_enum! {
70    /// An adapter for [`enum@proc_macro2::Spacing`].
71    #[derive(Clone, Copy)]
72    pub enum Spacing {
73        Alone,
74        Joint,
75    }
76}
77
78ast_struct! {
79    /// An adapter for [`struct@proc_macro2::Ident`].
80    #[derive(Clone, Eq, PartialEq)]
81    #[serde(transparent)]
82    pub struct Ident {
83        inner: String,
84    }
85}
86
87ast_struct! {
88    /// An adapter for [`struct@proc_macro2::Literal`].
89    #[derive(Clone)]
90    #[serde(transparent)]
91    pub struct Literal {
92        pub(crate) text: String,
93    }
94}
95
96impl Literal {
97    fn _new(text: String) -> Self {
98        Self { text }
99    }
100
101    pub(crate) fn u8_suffixed(n: u8) -> Self {
102        Self::_new(format!(concat!("{}", stringify!(u8)), n))
103    }
104
105    pub(crate) fn string(t: &str) -> Self {
106        let mut s = t.chars().flat_map(char::escape_default).collect::<String>();
107        s.push('"');
108        s.insert(0, '"');
109        Self::_new(s)
110    }
111
112    pub(crate) fn character(t: char) -> Self {
113        Self::_new(format!("'{}'", t.escape_default().collect::<String>()))
114    }
115
116    #[allow(clippy::match_overlapping_arm)]
117    pub(crate) fn byte_string(bytes: &[u8]) -> Self {
118        let mut escaped = "b\"".to_owned();
119        for b in bytes {
120            match *b {
121                b'\0' => escaped.push_str(r"\0"),
122                b'\t' => escaped.push_str(r"\t"),
123                b'\n' => escaped.push_str(r"\n"),
124                b'\r' => escaped.push_str(r"\r"),
125                b'"' => escaped.push_str("\\\""),
126                b'\\' => escaped.push_str("\\\\"),
127                b'\x20'..=b'\x7E' => escaped.push(*b as char),
128                _ => {
129                    let _ = write!(escaped, "\\x{b:02X}");
130                }
131            }
132        }
133        escaped.push('"');
134        Self::_new(escaped)
135    }
136}
137
138// TODO(semver): when release the next breaking release, remove this.
139impl fmt::Display for Literal {
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        fmt::Display::fmt(&self.text, f)
142    }
143}
144
145mod convert {
146    use super::*;
147
148    // TokenStream
149    syn_trait_impl!(proc_macro2::TokenStream);
150    impl From<&proc_macro2::TokenStream> for TokenStream {
151        fn from(other: &proc_macro2::TokenStream) -> Self {
152            Self::_new(other.clone().into_iter().map::<TokenTree, _>(|x| x.ref_into()).collect())
153        }
154    }
155    impl From<&TokenStream> for proc_macro2::TokenStream {
156        fn from(other: &TokenStream) -> Self {
157            other.inner.iter().map::<proc_macro2::TokenTree, _>(Into::into).collect()
158        }
159    }
160
161    // TokenTree
162    syn_trait_impl!(proc_macro2::TokenTree);
163    impl From<&proc_macro2::TokenTree> for TokenTree {
164        fn from(other: &proc_macro2::TokenTree) -> Self {
165            use super::TokenTree::*;
166            match other {
167                proc_macro2::TokenTree::Group(t) => Group(t.into()),
168                proc_macro2::TokenTree::Ident(t) => Ident(t.into()),
169                proc_macro2::TokenTree::Punct(t) => Punct(t.into()),
170                proc_macro2::TokenTree::Literal(t) => Literal(t.into()),
171            }
172        }
173    }
174    impl From<&TokenTree> for proc_macro2::TokenTree {
175        fn from(other: &TokenTree) -> Self {
176            use proc_macro2::TokenTree::*;
177            match other {
178                TokenTree::Group(t) => Group(t.into()),
179                TokenTree::Ident(t) => Ident(t.into()),
180                TokenTree::Punct(t) => Punct(t.into()),
181                TokenTree::Literal(t) => Literal(t.into()),
182            }
183        }
184    }
185
186    // Group
187    syn_trait_impl!(proc_macro2::Group);
188    impl From<&proc_macro2::Group> for Group {
189        fn from(other: &proc_macro2::Group) -> Self {
190            Self { delimiter: other.delimiter().ref_into(), stream: other.stream().ref_into() }
191        }
192    }
193    impl From<&Group> for proc_macro2::Group {
194        fn from(other: &Group) -> Self {
195            Self::new(other.delimiter.ref_into(), other.stream.ref_into())
196        }
197    }
198
199    // Delimiter
200    syn_trait_impl!(proc_macro2::Delimiter);
201    impl From<&proc_macro2::Delimiter> for Delimiter {
202        fn from(other: &proc_macro2::Delimiter) -> Self {
203            use super::Delimiter::*;
204            match other {
205                proc_macro2::Delimiter::Parenthesis => Parenthesis,
206                proc_macro2::Delimiter::Brace => Brace,
207                proc_macro2::Delimiter::Bracket => Bracket,
208                proc_macro2::Delimiter::None => None,
209            }
210        }
211    }
212    impl From<&Delimiter> for proc_macro2::Delimiter {
213        fn from(other: &Delimiter) -> Self {
214            use proc_macro2::Delimiter::*;
215            match other {
216                Delimiter::Parenthesis => Parenthesis,
217                Delimiter::Brace => Brace,
218                Delimiter::Bracket => Bracket,
219                Delimiter::None => None,
220            }
221        }
222    }
223
224    // Ident
225    syn_trait_impl!(proc_macro2::Ident);
226    impl From<&proc_macro2::Ident> for Ident {
227        fn from(other: &proc_macro2::Ident) -> Self {
228            Self { inner: other.to_string() }
229        }
230    }
231    impl From<&Ident> for proc_macro2::Ident {
232        fn from(other: &Ident) -> Self {
233            Self::new(&other.inner, Span::call_site())
234        }
235    }
236
237    // Punct
238    syn_trait_impl!(proc_macro2::Punct);
239    impl From<&proc_macro2::Punct> for Punct {
240        fn from(other: &proc_macro2::Punct) -> Self {
241            Self { op: other.as_char(), spacing: other.spacing().ref_into() }
242        }
243    }
244    impl From<&Punct> for proc_macro2::Punct {
245        fn from(other: &Punct) -> Self {
246            Self::new(other.op, other.spacing.ref_into())
247        }
248    }
249
250    // Spacing
251    syn_trait_impl!(proc_macro2::Spacing);
252    impl From<&proc_macro2::Spacing> for Spacing {
253        fn from(other: &proc_macro2::Spacing) -> Self {
254            use super::Spacing::*;
255            match other {
256                proc_macro2::Spacing::Alone => Alone,
257                proc_macro2::Spacing::Joint => Joint,
258            }
259        }
260    }
261    impl From<&Spacing> for proc_macro2::Spacing {
262        fn from(other: &Spacing) -> Self {
263            use proc_macro2::Spacing::*;
264            match other {
265                Spacing::Alone => Alone,
266                Spacing::Joint => Joint,
267            }
268        }
269    }
270
271    // Literal
272    syn_trait_impl!(proc_macro2::Literal);
273    impl From<&proc_macro2::Literal> for Literal {
274        fn from(other: &proc_macro2::Literal) -> Self {
275            Self { text: other.to_string() }
276        }
277    }
278    impl From<&Literal> for proc_macro2::Literal {
279        fn from(other: &Literal) -> Self {
280            use proc_macro2::*;
281            let stream = other.text.parse::<TokenStream>().unwrap();
282            match stream.into_iter().next().unwrap() {
283                TokenTree::Literal(l) => l,
284                _ => unreachable!(),
285            }
286        }
287    }
288}