structmeta_derive/
syn_utils.rs

1use proc_macro2::TokenStream;
2use quote::quote;
3use syn::{
4    punctuated::Punctuated, DeriveInput, Path, PathArguments, PathSegment, Result, Token, Type,
5    WherePredicate,
6};
7
8macro_rules! bail {
9    ($span:expr, $message:literal $(,)?) => {
10        return std::result::Result::Err(syn::Error::new($span, $message))
11    };
12    ($span:expr, $err:expr $(,)?) => {
13        return std::result::Result::Err(syn::Error::new($span, $err))
14    };
15    ($span:expr, $fmt:expr, $($arg:tt)*) => {
16        return std::result::Result::Err(syn::Error::new($span, std::format!($fmt, $($arg)*)))
17    };
18}
19
20pub fn into_macro_output(input: Result<TokenStream>) -> proc_macro::TokenStream {
21    match input {
22        Ok(s) => s,
23        Err(e) => e.to_compile_error(),
24    }
25    .into()
26}
27
28pub fn impl_trait(
29    input: &DeriveInput,
30    trait_path: &Path,
31    wheres: &[WherePredicate],
32    contents: TokenStream,
33) -> TokenStream {
34    let ty = &input.ident;
35    let (impl_g, ty_g, where_clause) = input.generics.split_for_impl();
36    let mut wheres = wheres.to_vec();
37    if let Some(where_clause) = where_clause {
38        wheres.extend(where_clause.predicates.iter().cloned());
39    }
40    let where_clause = if wheres.is_empty() {
41        quote! {}
42    } else {
43        quote! { where #(#wheres,)*}
44    };
45    quote! {
46        #[automatically_derived]
47        impl #impl_g #trait_path for #ty #ty_g #where_clause {
48            #contents
49        }
50    }
51}
52pub fn impl_trait_result(
53    input: &DeriveInput,
54    trait_path: &Path,
55    wheres: &[WherePredicate],
56    contents: TokenStream,
57    dump: bool,
58) -> Result<TokenStream> {
59    let ts = impl_trait(input, trait_path, wheres, contents);
60    if dump {
61        panic!("macro result: \n{ts}");
62    }
63    Ok(ts)
64}
65
66pub fn is_type(ty: &Type, ns: &[&[&str]], name: &str) -> bool {
67    if let Some(a) = get_arguments_of(ty, ns, name) {
68        a.is_empty()
69    } else {
70        false
71    }
72}
73pub fn get_arguments_of<'a>(ty: &'a Type, ns: &[&[&str]], name: &str) -> Option<&'a PathArguments> {
74    if let Type::Path(ty) = ty {
75        if ty.qself.is_some() {
76            return None;
77        }
78        let ss = &ty.path.segments;
79        if let Some(last) = ty.path.segments.last() {
80            if last.ident != name {
81                return None;
82            }
83            return if ns.iter().any(|ns| is_match_ns(ss, ns)) {
84                Some(&last.arguments)
85            } else {
86                None
87            };
88        }
89    }
90    None
91}
92pub fn is_match_ns(ss: &Punctuated<PathSegment, Token![::]>, ns: &[&str]) -> bool {
93    let mut i_ss = ss.len() - 1;
94    let mut i_ns = ns.len();
95    while i_ss > 0 && i_ns > 0 {
96        i_ns -= 1;
97        i_ss -= 1;
98        let s = &ss[i_ss];
99        if s.ident != ns[i_ns] || !s.arguments.is_empty() {
100            return false;
101        }
102    }
103    i_ss == 0
104}
105pub const NS_SYN: &[&[&str]] = &[&["syn"]];
106
107pub fn is_macro_delimiter(ty: &Type) -> bool {
108    is_type(ty, NS_SYN, "MacroDelimiter")
109}