aboutsummaryrefslogtreecommitdiff
path: root/derive/src
diff options
context:
space:
mode:
Diffstat (limited to 'derive/src')
-rw-r--r--derive/src/lib.rs36
-rw-r--r--derive/src/network/mod.rs12
-rw-r--r--derive/src/setters/mod.rs107
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();
+}