1use convert_case::Casing;
2use proc_macro2::TokenStream;
3use quote::quote as q;
4use syn::Ident;
5use syn::Token;
6
7struct Argument {
8 name: Ident,
9 ty: syn::Type,
10 enum_attr: Vec<proc_macro2::Group>,
11 to_owned: bool,
12}
13
14impl std::fmt::Debug for Argument {
15 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16 let t = &self.ty;
17 f.debug_struct("Argument")
18 .field("name", &self.name.to_string())
19 .field("ty", &format!("{}", q! {#t}))
20 .finish()
21 }
22}
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25enum ReceiverStyle {
26 Move,
27 Mut,
28 Ref,
29}
30
31struct Method {
32 name: Ident,
33 receiver_style: ReceiverStyle,
34 args: Vec<Argument>,
35 ret: Option<syn::Type>,
36 enum_attr: Vec<syn::Attribute>,
37 return_attr: Vec<syn::Attribute>,
38 doc_attr: Vec<syn::Attribute>,
39 r#async: bool,
40}
41
42impl Method {
43 fn variant_name(&self) -> proc_macro2::Ident {
44 let mut ident = quote::format_ident!(
45 "{}",
46 self.name
47 .to_string()
48 .to_case(convert_case::Case::UpperCamel)
49 );
50 ident.set_span(self.name.span());
51 ident
52 }
53}
54
55impl std::fmt::Debug for Method {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 f.debug_struct("Method")
58 .field("name", &self.name.to_string())
59 .field("receiver_style", &self.receiver_style)
60 .field("args", &self.args)
61 .finish()
62 }
63}
64
65pub struct InputData {
66 name: Ident,
68 generics: syn::Generics,
69 struct_args: syn::PathArguments,
70 methods: Vec<Method>,
71 params: Params,
72}
73
74impl InputData {
75 fn has_async_functions(&self) -> bool {
76 self.methods.iter().any(|x| x.r#async)
77 }
78}
79
80pub enum Proxy {
81 Trait(Token![trait], syn::Ident),
82}
83
84pub struct Params {
85 visibility: syn::Visibility,
86 returnval: Option<syn::Type>,
87 proxies: Vec<Proxy>,
88 enum_attr: Vec<syn::Attribute>,
89 enum_name: Ident,
90 context: Option<(syn::Ident, syn::Type)>,
91}
92
93pub mod generate;
94pub mod parse_args;
95pub mod parse_input;
96
97pub fn ctrlgen_impl(attrs: TokenStream, input: TokenStream) -> syn::Result<TokenStream> {
98 let params = syn::parse2(attrs)?;
99
100 let mut ret = TokenStream::new();
101 let mut imp: syn::ItemImpl = syn::parse2(input)?;
102 let input_data = InputData::parse_inherent_impl(&mut imp, params)?;
103
104 ret.extend(input_data.generate_enum());
105 ret.extend(input_data.generate_call_impl());
106 ret.extend(input_data.generate_proxies());
107 ret.extend(quote::quote! {#imp});
108
109 syn::Result::<TokenStream>::Ok(ret)
110}