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);