panda_macros/
panda_args.rs

1#[derive(FromField)]
2#[darling(attributes(arg))]
3struct DeriveArgs {
4    #[darling(default)]
5    about: Option<String>,
6    #[darling(default)]
7    default: Option<syn::Lit>,
8    #[darling(default)]
9    required: bool,
10    ident: Option<syn::Ident>,
11    ty: syn::Type,
12}
13
14fn derive_args_to_mappings(
15    DeriveArgs {
16        about,
17        default,
18        ident,
19        ty,
20        required,
21    }: DeriveArgs,
22) -> (syn::Stmt, syn::Ident) {
23    let name = &ident;
24    let default = if let Some(default) = default {
25        match default {
26            syn::Lit::Str(string) => quote!(::std::string::String::from(#string)),
27            default => quote!(#default),
28        }
29    } else {
30        quote!(Default::default())
31    };
32    let about = about.unwrap_or_default();
33    (
34        syn::parse_quote!(
35            let #name = <#ty as ::panda::panda_arg::GetPandaArg>::get_panda_arg(
36                __args_ptr,
37                stringify!(#name),
38                #default,
39                #about,
40                #required
41            );
42        ),
43        ident.unwrap(),
44    )
45}
46
47fn get_field_statements(
48    fields: &syn::Fields,
49) -> Result<(Vec<syn::Stmt>, Vec<syn::Ident>), darling::Error> {
50    Ok(fields
51        .iter()
52        .map(DeriveArgs::from_field)
53        .collect::<Result<Vec<_>, _>>()?
54        .into_iter()
55        .map(derive_args_to_mappings)
56        .unzip())
57}
58
59fn get_name(attrs: &[syn::Attribute]) -> Option<String> {
60    attrs
61        .iter()
62        .find(|attr| attr.path.get_ident().map(|x| *x == "name").unwrap_or(false))
63        .map(|attr| attr.parse_meta().ok())
64        .flatten()
65        .map(|meta| {
66            if let syn::Meta::NameValue(syn::MetaNameValue {
67                lit: syn::Lit::Str(s),
68                ..
69            }) = meta
70            {
71                Some(s.value())
72            } else {
73                None
74            }
75        })
76        .flatten()
77}
78
79#[proc_macro_derive(PandaArgs, attributes(name, arg))]
80pub fn derive_panda_args(input: TokenStream) -> TokenStream {
81    let input = syn::parse_macro_input!(input as syn::ItemStruct);
82
83    let name = match get_name(&input.attrs) {
84        Some(name) => name,
85        None => {
86            return quote!(compile_error!(
87                "Missing plugin name, add `#[name = ...]` above struct"
88            ))
89            .into()
90        }
91    };
92
93    let ident = &input.ident;
94
95    match get_field_statements(&input.fields) {
96        Ok((statements, fields)) => {
97            let format_args = iter::repeat("{}={}")
98                .take(statements.len())
99                .collect::<Vec<_>>()
100                .join(",");
101            quote!(
102                impl ::panda::PandaArgs for #ident {
103                    const PLUGIN_NAME: &'static str = #name;
104
105                    fn from_panda_args() -> Self {
106                        let name = ::std::ffi::CString::new(#name).unwrap();
107
108                        unsafe {
109                            let __args_ptr = ::panda::sys::panda_get_args(name.as_ptr());
110
111                            #(
112                                #statements
113                            )*
114
115                            ::panda::sys::panda_free_args(__args_ptr);
116
117                            Self {
118                                #(#fields),*
119                            }
120                        }
121                    }
122
123                    fn to_panda_args_str(&self) -> ::std::string::String {
124                        format!(
125                            concat!(#name, ":", #format_args),
126                            #(
127                                stringify!(#fields), self.#fields
128                            ),*
129                       )
130                    }
131
132                    fn to_panda_args(&self) -> ::std::vec::Vec<(&'static str, ::std::string::String)> {
133                        ::std::vec![
134                            #(
135                                (stringify!(#fields), self.#fields.to_string()),
136                            )*
137                        ]
138                    }
139                }
140            ).into()
141        }
142        Err(err) => err.write_errors().into(),
143    }
144}