1use proc_macro::TokenStream;
2use proc_macro2::TokenStream as TokenStream2;
3use quote::{ToTokens, quote};
4use syn::{Error, Item, Result, parse2, spanned::Spanned};
5
6#[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#[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::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}