1use std::iter;
2
3use proc_macro2::{Ident, Span, TokenStream, TokenTree};
4use quote::{format_ident, quote, ToTokens};
5use syn::{
6 parenthesized,
7 parse::{Parse, ParseStream, Parser},
8 punctuated::Punctuated,
9 spanned::Spanned,
10 token, Attribute, Error, Pat, PatIdent, Token,
11};
12
13pub fn path_to_single_string(path: &syn::Path) -> Option<String> {
14 if path.leading_colon.is_some() {
15 return None;
16 }
17 let mut it = path.segments.iter();
18 let segment = it.next()?;
19 if it.next().is_some() {
20 return None;
22 }
23 if segment.arguments != syn::PathArguments::None {
24 return None;
25 }
26 Some(segment.ident.to_string())
27}
28
29pub fn ident_to_type(ident: syn::Ident) -> syn::Type {
30 let mut path = syn::Path {
31 leading_colon: None,
32 segments: Default::default(),
33 };
34 path.segments.push(syn::PathSegment {
35 ident,
36 arguments: Default::default(),
37 });
38 syn::Type::Path(syn::TypePath { qself: None, path })
39}
40
41pub fn empty_type() -> syn::Type {
42 syn::TypeTuple {
43 paren_token: Default::default(),
44 elems: Default::default(),
45 }
46 .into()
47}
48
49pub fn type_tuple(elems: impl Iterator<Item = syn::Type>) -> syn::TypeTuple {
50 let mut result = syn::TypeTuple {
51 paren_token: Default::default(),
52 elems: elems.collect(),
53 };
54 if !result.elems.empty_or_trailing() {
55 result.elems.push_punct(Default::default());
56 }
57 result
58}
59
60pub fn empty_type_tuple() -> syn::TypeTuple {
61 syn::TypeTuple {
62 paren_token: Default::default(),
63 elems: Default::default(),
64 }
65}
66
67pub fn modify_types_generics_hack<F>(ty_generics: &syn::TypeGenerics, mut mutator: F) -> syn::AngleBracketedGenericArguments
68where
69 F: FnMut(&mut syn::punctuated::Punctuated<syn::GenericArgument, syn::token::Comma>),
70{
71 let mut abga: syn::AngleBracketedGenericArguments =
72 syn::parse2(ty_generics.to_token_stream()).unwrap_or_else(|_| syn::AngleBracketedGenericArguments {
73 colon2_token: None,
74 lt_token: Default::default(),
75 args: Default::default(),
76 gt_token: Default::default(),
77 });
78 mutator(&mut abga.args);
79 abga
80}
81
82pub fn strip_raw_ident_prefix(mut name: String) -> String {
83 if name.starts_with("r#") {
84 name.replace_range(0..2, "");
85 }
86 name
87}
88
89pub fn first_visibility(visibilities: &[Option<&syn::Visibility>]) -> proc_macro2::TokenStream {
90 let vis = visibilities
91 .iter()
92 .flatten()
93 .next()
94 .expect("need at least one visibility in the list");
95
96 vis.to_token_stream()
97}
98
99pub fn public_visibility() -> syn::Visibility {
100 syn::Visibility::Public(syn::token::Pub::default())
101}
102
103pub fn expr_to_lit_string(expr: &syn::Expr) -> Result<String, Error> {
104 match expr {
105 syn::Expr::Lit(lit) => match &lit.lit {
106 syn::Lit::Str(str) => Ok(str.value()),
107 _ => Err(Error::new_spanned(expr, "attribute only allows str values")),
108 },
109 _ => Err(Error::new_spanned(expr, "attribute only allows str values")),
110 }
111}
112
113pub enum AttrArg {
114 Flag(Ident),
115 KeyValue(KeyValue),
116 Sub(SubAttr),
117 Not { not: Token![!], name: Ident },
118}
119
120impl AttrArg {
121 pub fn name(&self) -> &Ident {
122 match self {
123 AttrArg::Flag(name) => name,
124 AttrArg::KeyValue(KeyValue { name, .. }) => name,
125 AttrArg::Sub(SubAttr { name, .. }) => name,
126 AttrArg::Not { name, .. } => name,
127 }
128 }
129
130 pub fn incorrect_type(&self) -> syn::Error {
131 let message = match self {
132 AttrArg::Flag(name) => format!("{:?} is not supported as a flag", name.to_string()),
133 AttrArg::KeyValue(KeyValue { name, .. }) => format!("{:?} is not supported as key-value", name.to_string()),
134 AttrArg::Sub(SubAttr { name, .. }) => format!("{:?} is not supported as nested attribute", name.to_string()),
135 AttrArg::Not { name, .. } => format!("{:?} cannot be nullified", name.to_string()),
136 };
137 syn::Error::new_spanned(self, message)
138 }
139
140 pub fn flag(self) -> syn::Result<Ident> {
141 if let Self::Flag(name) = self {
142 Ok(name)
143 } else {
144 Err(self.incorrect_type())
145 }
146 }
147
148 pub fn key_value(self) -> syn::Result<KeyValue> {
149 if let Self::KeyValue(key_value) = self {
150 Ok(key_value)
151 } else {
152 Err(self.incorrect_type())
153 }
154 }
155
156 pub fn key_value_or_not(self) -> syn::Result<Option<KeyValue>> {
157 match self {
158 Self::KeyValue(key_value) => Ok(Some(key_value)),
159 Self::Not { .. } => Ok(None),
160 _ => Err(self.incorrect_type()),
161 }
162 }
163
164 pub fn sub_attr(self) -> syn::Result<SubAttr> {
165 if let Self::Sub(sub_attr) = self {
166 Ok(sub_attr)
167 } else {
168 Err(self.incorrect_type())
169 }
170 }
171
172 pub fn apply_flag_to_field(self, field: &mut Option<Span>, caption: &str) -> syn::Result<()> {
173 match self {
174 AttrArg::Flag(flag) => {
175 if field.is_none() {
176 *field = Some(flag.span());
177 Ok(())
178 } else {
179 Err(Error::new(
180 flag.span(),
181 format!("Illegal setting - field is already {caption}"),
182 ))
183 }
184 }
185 AttrArg::Not { .. } => {
186 *field = None;
187 Ok(())
188 }
189 _ => Err(self.incorrect_type()),
190 }
191 }
192
193 pub fn apply_potentialy_empty_sub_to_field<T: ApplyMeta>(
194 self,
195 field: &mut Option<T>,
196 caption: &str,
197 init: impl FnOnce(Span) -> T,
198 ) -> syn::Result<()> {
199 match self {
200 AttrArg::Sub(sub) => {
201 if field.is_none() {
202 let mut value = init(sub.span());
203 value.apply_sub_attr(sub)?;
204 *field = Some(value);
205 Ok(())
206 } else {
207 Err(Error::new(
208 sub.span(),
209 format!("Illegal setting - field is already {caption}"),
210 ))
211 }
212 }
213 AttrArg::Flag(flag) => {
214 if field.is_none() {
215 *field = Some(init(flag.span()));
216 Ok(())
217 } else {
218 Err(Error::new(
219 flag.span(),
220 format!("Illegal setting - field is already {caption}"),
221 ))
222 }
223 }
224 AttrArg::Not { .. } => {
225 *field = None;
226 Ok(())
227 }
228 _ => Err(self.incorrect_type()),
229 }
230 }
231}
232
233pub struct KeyValue {
234 pub name: Ident,
235 pub eq: Token![=],
236 pub value: TokenStream,
237}
238
239impl KeyValue {
240 pub fn parse_value<T: Parse>(self) -> syn::Result<T> {
241 syn::parse2(self.value)
242 }
243}
244
245impl ToTokens for KeyValue {
246 fn to_tokens(&self, tokens: &mut TokenStream) {
247 self.name.to_tokens(tokens);
248 self.eq.to_tokens(tokens);
249 self.value.to_tokens(tokens);
250 }
251}
252
253impl Parse for KeyValue {
254 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
255 Ok(Self {
256 name: input.parse()?,
257 eq: input.parse()?,
258 value: input.parse()?,
259 })
260 }
261}
262
263pub struct SubAttr {
264 pub name: Ident,
265 pub paren: token::Paren,
266 pub args: TokenStream,
267}
268
269impl SubAttr {
270 pub fn args<T: Parse>(self) -> syn::Result<impl IntoIterator<Item = T>> {
271 Punctuated::<T, Token![,]>::parse_terminated.parse2(self.args)
272 }
273 pub fn undelimited<T: Parse>(self) -> syn::Result<impl IntoIterator<Item = T>> {
274 (|p: ParseStream| iter::from_fn(|| (!p.is_empty()).then(|| p.parse())).collect::<syn::Result<Vec<T>>>()).parse2(self.args)
275 }
276}
277
278impl ToTokens for SubAttr {
279 fn to_tokens(&self, tokens: &mut TokenStream) {
280 self.name.to_tokens(tokens);
281 self.paren.surround(tokens, |t| self.args.to_tokens(t));
282 }
283}
284
285fn get_cursor_after_parsing<P: Parse + Spanned>(input: syn::parse::ParseBuffer) -> syn::Result<syn::buffer::Cursor> {
286 let parse_attempt: P = input.parse()?;
287 let cursor = input.cursor();
288 if cursor.eof() || input.peek(Token![,]) {
289 Ok(cursor)
290 } else {
291 Err(syn::Error::new(
292 parse_attempt.span(),
293 "does not end with comma or end of section",
294 ))
295 }
296}
297
298fn get_token_stream_up_to_cursor(input: syn::parse::ParseStream, cursor: syn::buffer::Cursor) -> syn::Result<TokenStream> {
299 Ok(core::iter::from_fn(|| {
300 if input.cursor() < cursor {
301 input.parse::<TokenTree>().ok()
302 } else {
303 None
304 }
305 })
306 .collect())
307}
308
309impl Parse for AttrArg {
310 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
311 if input.peek(Token![!]) {
312 Ok(Self::Not {
313 not: input.parse()?,
314 name: input.parse()?,
315 })
316 } else {
317 let name = input.parse()?;
318 if input.peek(Token![,]) || input.is_empty() {
319 Ok(Self::Flag(name))
320 } else if input.peek(token::Paren) {
321 let args;
322 Ok(Self::Sub(SubAttr {
323 name,
324 paren: parenthesized!(args in input),
325 args: args.parse()?,
326 }))
327 } else if input.peek(Token![=]) {
328 Ok(Self::KeyValue(KeyValue {
331 name,
332 eq: input.parse()?,
333 value: {
334 let cursor = get_cursor_after_parsing::<syn::Type>(input.fork())
335 .or_else(|_| get_cursor_after_parsing::<syn::Expr>(input.fork()))?;
336 get_token_stream_up_to_cursor(input, cursor)?
337 },
338 }))
339 } else {
340 Err(input.error("expected !<ident>, <ident>=<value> or <ident>(…)"))
341 }
342 }
343 }
344}
345
346impl ToTokens for AttrArg {
347 fn to_tokens(&self, tokens: &mut TokenStream) {
348 match self {
349 AttrArg::Flag(flag) => flag.to_tokens(tokens),
350 AttrArg::KeyValue(kv) => kv.to_tokens(tokens),
351 AttrArg::Sub(sub) => sub.to_tokens(tokens),
352 AttrArg::Not { not, name } => {
353 not.to_tokens(tokens);
354 name.to_tokens(tokens);
355 }
356 }
357 }
358}
359
360pub trait ApplyMeta {
361 fn apply_meta(&mut self, expr: AttrArg) -> Result<(), Error>;
362
363 fn apply_sub_attr(&mut self, sub_attr: SubAttr) -> syn::Result<()> {
364 for arg in sub_attr.args()? {
365 self.apply_meta(arg)?;
366 }
367 Ok(())
368 }
369
370 fn apply_subsections(&mut self, list: &syn::MetaList) -> syn::Result<()> {
371 if list.tokens.is_empty() {
372 return Err(syn::Error::new_spanned(list, "Expected builder(…)"));
373 }
374
375 let parser = syn::punctuated::Punctuated::<_, syn::token::Comma>::parse_terminated;
376 let exprs = parser.parse2(list.tokens.clone())?;
377 for expr in exprs {
378 self.apply_meta(expr)?;
379 }
380
381 Ok(())
382 }
383
384 fn apply_attr(&mut self, attr: &Attribute) -> syn::Result<()> {
385 match &attr.meta {
386 syn::Meta::List(list) => self.apply_subsections(list),
387 meta => Err(Error::new_spanned(meta, "Expected builder(…)")),
388 }
389 }
390}
391
392pub fn pat_to_ident(i: usize, pat: &Pat) -> Ident {
393 if let Pat::Ident(PatIdent { ident, .. }) = pat {
394 ident.clone()
395 } else {
396 format_ident!("__{i}", span = pat.span())
397 }
398}
399
400pub fn phantom_data_for_generics(generics: &syn::Generics) -> proc_macro2::TokenStream {
401 let phantom_generics = generics.params.iter().filter_map(|param| match param {
402 syn::GenericParam::Lifetime(lifetime) => {
403 let lifetime = &lifetime.lifetime;
404 Some(quote!(&#lifetime ()))
405 }
406 syn::GenericParam::Type(ty) => {
407 let ty = &ty.ident;
408 Some(ty.to_token_stream())
409 }
410 syn::GenericParam::Const(_cnst) => None,
411 });
412 quote!(::core::marker::PhantomData<(#( ::core::marker::PhantomData<#phantom_generics> ),*)>)
413}