mock_syn_common/
syn.rs

1use std::ops::Deref;
2
3use proc_macro2::{Ident, TokenStream};
4use quote::{IdentFragment, ToTokens};
5use syn::{
6    parenthesized,
7    parse::{Parse, ParseStream},
8    Error, Index, Result,
9};
10
11#[derive(Clone, Debug)]
12pub struct Parenthesized<T: Parse>(T);
13
14impl<T: Parse> Deref for Parenthesized<T> {
15    type Target = T;
16
17    fn deref(&self) -> &Self::Target {
18        &self.0
19    }
20}
21
22impl<T: Parse> Parse for Parenthesized<T> {
23    fn parse(input: ParseStream) -> Result<Self> {
24        let content;
25        let _ = parenthesized!(content in input);
26        content.parse().map(Self)
27    }
28}
29
30impl<T: Parse + ToTokens> ToTokens for Parenthesized<T> {
31    fn to_tokens(&self, tokens: &mut TokenStream) {
32        self.0.to_tokens(tokens);
33    }
34}
35
36macro_rules! parenthesized_try_into {
37    ($local:ty as $remote:ty) => {
38        impl From<$remote> for Parenthesized<$local>
39        where
40            $local: From<$remote>,
41        {
42            fn from(value: $remote) -> Parenthesized<$local> {
43                Parenthesized(From::from(value))
44            }
45        }
46
47        impl TryInto<$remote> for Parenthesized<$local>
48        where
49            $local: TryInto<$remote>,
50        {
51            type Error = <$local as TryInto<$remote>>::Error;
52
53            fn try_into(self) -> std::result::Result<$remote, Self::Error> {
54                self.0.try_into()
55            }
56        }
57
58        impl<'a> TryInto<$remote> for &'a Parenthesized<$local>
59        where
60            &'a $local: TryInto<$remote>,
61        {
62            type Error = <&'a $local as TryInto<$remote>>::Error;
63
64            fn try_into(self) -> std::result::Result<$remote, Self::Error> {
65                self.deref().try_into()
66            }
67        }
68
69        impl<'a> TryInto<&'a $remote> for &'a Parenthesized<$local>
70        where
71            &'a $local: TryInto<&'a $remote>,
72        {
73            type Error = <&'a $local as TryInto<&'a $remote>>::Error;
74
75            fn try_into(self) -> std::result::Result<&'a $remote, Self::Error> {
76                self.deref().try_into()
77            }
78        }
79    };
80}
81
82#[derive(Clone, Debug, PartialEq, Eq, Hash)]
83pub enum IdentIndex {
84    Ident(Ident),
85    Index(Index),
86}
87
88impl IdentIndex {
89    pub fn as_ident(&self) -> Result<&Ident> {
90        match self {
91            Self::Ident(v) => Ok(v),
92            Self::Index(v) => Err(Error::new(v.span, "Expected an Ident")),
93        }
94    }
95
96    pub fn as_index(&self) -> Result<&Index> {
97        match self {
98            Self::Ident(v) => Err(Error::new(v.span(), "Expected an Ident")),
99            Self::Index(v) => Ok(v),
100        }
101    }
102}
103
104impl IdentFragment for IdentIndex {
105    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
106        match self {
107            Self::Ident(v) => <Ident as IdentFragment>::fmt(v, f),
108            Self::Index(v) => <Index as IdentFragment>::fmt(v, f),
109        }
110    }
111}
112
113impl Parse for IdentIndex {
114    fn parse(input: ParseStream) -> Result<Self> {
115        if input.peek(syn::Ident) {
116            input.parse().map(Self::Ident)
117        } else if input.peek(syn::LitInt) {
118            input.parse().map(Self::Index)
119        } else {
120            Err(input.error("expected identifier or integer"))
121        }
122    }
123}
124
125impl ToTokens for IdentIndex {
126    fn to_tokens(&self, tokens: &mut TokenStream) {
127        match self {
128            Self::Ident(v) => v.to_tokens(tokens),
129            Self::Index(v) => v.to_tokens(tokens),
130        }
131    }
132}
133
134impl PartialEq<str> for IdentIndex {
135    fn eq(&self, other: &str) -> bool {
136        match self {
137            Self::Ident(v) => v == other,
138            Self::Index(v) => v.index.to_string() == other,
139        }
140    }
141}
142
143impl PartialEq<u32> for IdentIndex {
144    fn eq(&self, other: &u32) -> bool {
145        match self {
146            Self::Ident(..) => false,
147            Self::Index(Index { index, .. }) => index == other,
148        }
149    }
150}
151
152macro_rules! convert {
153    ($local:ident::$method:ident as $remote:ident) => {
154        impl From<$remote> for $local {
155            fn from(value: $remote) -> Self {
156                Self::$remote(value)
157            }
158        }
159
160        impl TryInto<$remote> for $local {
161            type Error = Error;
162
163            fn try_into(self) -> Result<$remote> {
164                self.$method().cloned()
165            }
166        }
167
168        impl TryInto<$remote> for &$local {
169            type Error = Error;
170
171            fn try_into(self) -> Result<$remote> {
172                self.$method().cloned()
173            }
174        }
175
176        impl<'a> TryInto<&'a $remote> for &'a $local {
177            type Error = Error;
178
179            fn try_into(self) -> Result<&'a $remote> {
180                self.$method()
181            }
182        }
183    };
184}
185
186convert!(IdentIndex::as_ident as Ident);
187convert!(IdentIndex::as_index as Index);
188
189parenthesized_try_into!(IdentIndex as Ident);
190parenthesized_try_into!(IdentIndex as Index);