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}