diff options
Diffstat (limited to 'derive/src')
| -rw-r--r-- | derive/src/lib.rs | 36 | ||||
| -rw-r--r-- | derive/src/network/mod.rs | 12 | ||||
| -rw-r--r-- | derive/src/setters/mod.rs | 107 |
3 files changed, 153 insertions, 2 deletions
diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 606e8e3..dc6e9fe 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -3,13 +3,14 @@ use syn; mod from; mod network; mod sync; +mod setters; #[proc_macro_derive(NetworkList, attributes(network))] pub fn network_list(input: proc_macro::TokenStream) -> proc_macro::TokenStream { network::network_list(input) } -#[proc_macro_derive(NetworkMap, attributes(network))] +#[proc_macro_derive(NetworkMap, attributes(network, quassel))] pub fn network_map(input: proc_macro::TokenStream) -> proc_macro::TokenStream { network::network_map(input) } @@ -19,7 +20,40 @@ pub fn from(input: proc_macro::TokenStream) -> proc_macro::TokenStream { from::from(input) } +#[proc_macro_derive(Setters, attributes(quassel))] +pub fn setters(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + setters::setters(input) +} + #[proc_macro] pub fn sync(input: proc_macro::TokenStream) -> proc_macro::TokenStream { sync::sync(input) } + +use darling::FromField; + +#[derive(Debug, FromField)] +#[darling(attributes(quassel))] +struct QuasselField { + ident: Option<syn::Ident>, + ty: syn::Type, + + #[darling(default)] + name: String, +} + +impl QuasselField { + pub fn parse(input: &syn::DeriveInput) -> Vec<QuasselField> { + match &input.data { + syn::Data::Struct(data) => match &data.fields { + syn::Fields::Named(fields) => fields + .named + .iter() + .map(|field| QuasselField::from_field(field).expect("Could not parse quassel field")) + .collect(), + _ => panic!("quassel: not a named field"), + }, + _ => panic!("quassel: not a Struct"), + } + } +} diff --git a/derive/src/network/mod.rs b/derive/src/network/mod.rs index ff31957..7932a12 100644 --- a/derive/src/network/mod.rs +++ b/derive/src/network/mod.rs @@ -83,7 +83,17 @@ pub fn network_map(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let network = Network::from_derive_input(&input).unwrap(); - let fields = parse_fields(&input); + let mut fields = parse_fields(&input); + let quassel_fields = super::QuasselField::parse(&input); + + fields + .iter_mut() + .zip(quassel_fields) + .for_each(|(field, qfield)| { + if field.rename.is_none() { + field.rename = Some(qfield.name) + } + }); let name = &input.ident; diff --git a/derive/src/setters/mod.rs b/derive/src/setters/mod.rs new file mode 100644 index 0000000..4436c3c --- /dev/null +++ b/derive/src/setters/mod.rs @@ -0,0 +1,107 @@ +use darling::FromField; +use proc_macro2::Span; +use quote::quote; +use syn::{self, parse_macro_input}; + +#[derive(Debug, FromField)] +#[darling(attributes(setter))] +pub struct SetterField { + #[darling(default)] + skip: bool, + + #[darling(default)] + /// name for the variable + /// mainly to show up in the docs + /// by default using the last word of the name + var_name: Option<syn::Ident>, +} + +impl SetterField { + pub fn parse(input: &syn::DeriveInput) -> Vec<SetterField> { + match &input.data { + syn::Data::Struct(data) => match &data.fields { + syn::Fields::Named(fields) => fields + .named + .iter() + .map(|field| { + SetterField::from_field(field).expect("Could not parse setter field") + }) + .collect(), + _ => panic!("setter: not a named field"), + }, + _ => panic!("setter: not a Struct"), + } + } +} + +pub fn setters(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as syn::DeriveInput); + + let setter_fields = SetterField::parse(&input); + let quassel_fields = super::QuasselField::parse(&input); + + let generated: Vec<proc_macro2::TokenStream> = setter_fields + .iter() + .zip(quassel_fields) + .filter(|(setter_field, _)| !setter_field.skip) + .map(|(setter_field, quassel_field)| { + let name: String = { + let mut name = quassel_field.name.chars(); + + format!( + "set{}{}", + name.next().unwrap().to_ascii_uppercase(), + name.as_str() + ) + }; + + let var_name = syn::Ident::new( + &setter_field + .var_name + .as_ref() + .map(|v| v.to_string()) + .unwrap_or({ + let mut res = String::new(); + + for c in quassel_field.name.chars().rev() { + if c <= 'Z' && c >= 'A' { + res.push(c.to_ascii_lowercase()); + break; + } else { + res.push(c); + } + } + + res.chars().rev().collect() + }), + Span::call_site(), + ); + + let ident = quassel_field + .ident + .as_ref() + .expect("failed to get quassel field ident"); + let ty = &quassel_field.ty; + + let fn_name = syn::Ident::new(&format!("set_{}", ident), Span::call_site()); + + quote! { + pub fn #fn_name(&mut self, #var_name: #ty) { + #[cfg(feature = "server")] + self.send_sync(#name, vec![#var_name.clone().into()]); + + self.#ident = #var_name; + } + } + }) + .collect(); + + let struct_name = &input.ident; + let gen = quote! { + impl #struct_name { + #(#generated)* + } + }; + + return gen.into(); +} |
