quoth_macros/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::TokenStream as TokenStream2;
3use quote::{ToTokens, quote};
4use syn::{Error, Item, Result, parse2, spanned::Spanned};
5
6/// Derives [`Display`](core::fmt::Display) and [`FromStr`](core::str::FromStr) based on the
7/// the `parse()` and `unparse()` implementations of `Parsable` for this type, respectively.
8#[proc_macro_derive(ParsableExt)]
9pub fn derive_parsable_ext(tokens: TokenStream) -> TokenStream {
10    match derive_parsable_ext_internal(tokens.into()) {
11        Ok(tokens) => tokens,
12        Err(err) => err.to_compile_error(),
13    }
14    .into()
15}
16
17fn derive_parsable_ext_internal(tokens: TokenStream2) -> Result<TokenStream2> {
18    let item = parse2::<Item>(tokens)?;
19    let (ident, generics) = match item {
20        Item::Enum(item_enum) => (item_enum.ident, item_enum.generics),
21        Item::Struct(item_struct) => (item_struct.ident, item_struct.generics),
22        _ => return Err(Error::new(item.span(), "expected struct or enum")),
23    };
24    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
25    let tokens = quote! {
26        impl #impl_generics core::str::FromStr for #ident #ty_generics #where_clause {
27            type Err = quoth::Error;
28
29            fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
30                quoth::parse(s)
31            }
32        }
33
34        impl #impl_generics core::fmt::Display for #ident #ty_generics #where_clause {
35            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
36                self.unparse(f)
37            }
38        }
39    };
40    Ok(tokens)
41}
42
43/// Automatically derives `Spanned` for the annotated type. This will work as long as there is
44/// some struct field of type `Span`.
45#[proc_macro_derive(Spanned)]
46pub fn derive_spanned(tokens: TokenStream) -> TokenStream {
47    match derive_spanned_internal(tokens.into()) {
48        Ok(tokens) => tokens,
49        Err(err) => err.to_compile_error(),
50    }
51    .into()
52}
53
54fn derive_spanned_internal(tokens: TokenStream2) -> Result<TokenStream2> {
55    let item = parse2::<Item>(tokens)?;
56    let (field_name, ident, generics) = match item {
57        // Item::Enum(item_enum) => (item_enum.ident, item_enum.generics),
58        Item::Struct(item_struct) => {
59            let mut i: usize = 0;
60            let field_name = item_struct
61                .fields
62                .iter()
63                .find_map(|field| {
64                    i += 1;
65                    if field
66                        .ty
67                        .to_token_stream()
68                        .to_string()
69                        .trim()
70                        .ends_with("Span")
71                        || field.ident.to_token_stream().to_string().trim() == "span"
72                    {
73                        if let Some(ident) = field.ident.as_ref() {
74                            Some(quote!(self.#ident))
75                        } else {
76                            let lit = syn::Index::from(i - 1);
77                            Some(quote!(self.#lit))
78                        }
79                    } else {
80                        None
81                    }
82                })
83                .ok_or_else(|| {
84                    Error::new(item_struct.span(), "expected a field of type `quoth::Span`")
85                })?
86                .clone();
87            (field_name, item_struct.ident, item_struct.generics)
88        }
89        _ => return Err(Error::new(item.span(), "expected struct")),
90    };
91    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
92    let tokens = quote! {
93        impl #impl_generics quoth::Spanned for #ident #ty_generics #where_clause {
94            fn span(&self) -> quoth::Span {
95                #field_name.clone()
96            }
97        }
98    };
99    Ok(tokens)
100}