aboutsummaryrefslogtreecommitdiff
path: root/derive
diff options
context:
space:
mode:
Diffstat (limited to 'derive')
-rw-r--r--derive/Cargo.toml14
-rw-r--r--derive/src/from_network_impl.rs127
-rw-r--r--derive/src/lib.rs144
-rw-r--r--derive/src/to_network_impl.rs96
4 files changed, 381 insertions, 0 deletions
diff --git a/derive/Cargo.toml b/derive/Cargo.toml
new file mode 100644
index 0000000..bc4ed2f
--- /dev/null
+++ b/derive/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "libquassel-derive"
+version = "0.1.0"
+authors = ["Max Audron <audron@cocaine.farm>"]
+edition = "2018"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+syn = { version = "1.0", features = ["full", "extra-traits", "derive"] }
+quote = "1.0"
+proc-macro2 = "1.0"
+darling = "0.12"
diff --git a/derive/src/from_network_impl.rs b/derive/src/from_network_impl.rs
new file mode 100644
index 0000000..c3a3be2
--- /dev/null
+++ b/derive/src/from_network_impl.rs
@@ -0,0 +1,127 @@
+use proc_macro2::TokenStream;
+use quote::quote;
+
+use crate::{get_field_type, NetworkField};
+
+pub(crate) fn map(fields: &Vec<NetworkField>) -> Vec<TokenStream> {
+ fields
+ .iter()
+ .map(|field| {
+ let field_rename = match &field.rename {
+ Some(name) => name.clone(),
+ None => format!("{}", field.ident.as_ref().unwrap()).into(),
+ };
+
+ let field_name = field.ident.as_ref().unwrap();
+
+ let field_type = get_field_type(&field);
+
+ if field.from_map.is_some() {
+ let field_map: syn::ExprClosure =
+ syn::parse_str(&field.from_map.as_ref().unwrap()).unwrap();
+
+ quote! {
+ #field_name: match &input.get(#field_rename).unwrap() {
+ crate::primitive::Variant::#field_type(input) => input.iter().map(#field_map).collect(),
+ _ => unimplemented!()
+ },
+ }
+ } else {
+ quote! {
+ #field_name: match &input.get(#field_rename).unwrap() {
+ crate::primitive::Variant::#field_type(input) => input.clone(),
+ _ => unimplemented!()
+ },
+ }
+ }
+ })
+ .collect()
+}
+
+pub(crate) fn map_list(fields: &Vec<NetworkField>) -> Vec<TokenStream> {
+ fields
+ .iter()
+ .map(|field| {
+ let field_rename = match &field.rename {
+ Some(name) => name.clone(),
+ None => format!("{}", field.ident.as_ref().unwrap()).into(),
+ };
+
+ let field_name = field.ident.as_ref().unwrap();
+
+ let field_type = get_field_type(&field);
+
+ if field.from_map.is_some() {
+ let field_map: syn::ExprClosure =
+ syn::parse_str(&field.from_map.as_ref().unwrap()).unwrap();
+
+ quote! {
+ #field_name: match input.get(#field_rename).unwrap() {
+ crate::primitive::Variant::VariantList(input) => match &input.remove(0) {
+ crate::primitive::Variant::#field_type(input) => input.iter().map(#field_map).collect(),
+ _ => unimplemented!()
+ },
+ _ => unimplemented!()
+ },
+ }
+ } else {
+ quote! {
+ #field_name: match input.get(#field_rename).unwrap() {
+ crate::primitive::Variant::VariantList(input) => match &input.remove(0) {
+ crate::primitive::Variant::#field_type(input) => input.clone(),
+ _ => unimplemented!()
+ },
+ _ => unimplemented!()
+ },
+ }
+ }
+ })
+ .collect()
+}
+
+pub(crate) fn list(fields: &Vec<NetworkField>) -> Vec<TokenStream> {
+ fields
+ .iter()
+ .map(|field| {
+ let field_rename = match &field.rename {
+ Some(name) => name.clone(),
+ None => format!("{}", field.ident.as_ref().unwrap()).into(),
+ };
+
+ let field_name = field.ident.as_ref().unwrap();
+
+ let field_type = get_field_type(&field);
+
+ if field.from_map.is_some() {
+ let field_map: syn::ExprClosure =
+ syn::parse_str(&field.from_map.as_ref().unwrap()).unwrap();
+
+ quote! {
+ #field_name: match_variant!(
+ input
+ .iter()
+ .nth(input
+ .iter()
+ .position(|x| *x == crate::primitive::Variant::ByteArray(#field_rename.to_string()))
+ .unwrap())
+ .unwrap(),
+ crate::primitive::Variant::#field_type
+ ).iter().map(#field_map).collect(),
+ }
+ } else {
+ quote! {
+ #field_name: match_variant!(
+ input
+ .iter()
+ .nth(input
+ .iter()
+ .position(|x| *x == Variant::ByteArray(#field_rename.to_string()))
+ .unwrap() + 1)
+ .unwrap(),
+ crate::primitive::Variant::#field_type
+ ),
+ }
+ }
+ })
+ .collect()
+}
diff --git a/derive/src/lib.rs b/derive/src/lib.rs
new file mode 100644
index 0000000..10605a4
--- /dev/null
+++ b/derive/src/lib.rs
@@ -0,0 +1,144 @@
+use quote::quote;
+use syn;
+
+use syn::parse_macro_input;
+
+use darling::{FromDeriveInput, FromField, FromMeta};
+
+mod from_network_impl;
+mod to_network_impl;
+
+#[derive(Debug, FromDeriveInput)]
+#[darling(attributes(network), supports(struct_any))]
+struct Network {
+ ident: syn::Ident,
+ attrs: Vec<syn::Attribute>,
+ repr: Repr,
+}
+
+#[derive(Debug, Clone, Copy, FromMeta)]
+#[darling(default)]
+enum Repr {
+ Aos,
+ List,
+ Map,
+ Maplist,
+}
+
+impl Default for Repr {
+ fn default() -> Self {
+ Repr::List
+ }
+}
+
+#[derive(Debug, FromField)]
+#[darling(attributes(network))]
+struct NetworkField {
+ ident: Option<syn::Ident>,
+ ty: syn::Type,
+
+ #[darling(default)]
+ rename: Option<String>,
+ #[darling(default)]
+ override_type: Option<String>,
+ #[darling(default)]
+ to_map: Option<String>,
+ #[darling(default)]
+ from_map: Option<String>,
+}
+
+#[proc_macro_derive(Network, attributes(network))]
+pub fn network(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ let input = parse_macro_input!(input as syn::DeriveInput);
+ // println!("{:#?}", input);
+
+ let network = Network::from_derive_input(&input).unwrap();
+ // println!("{:#?}", network);
+
+ let fields: Vec<NetworkField> = match &input.data {
+ syn::Data::Struct(data) => match &data.fields {
+ syn::Fields::Named(fields) => fields
+ .named
+ .iter()
+ .map(|field| NetworkField::from_field(field).expect("Could not parse field"))
+ .collect(),
+ _ => unimplemented!(),
+ },
+ _ => unimplemented!(),
+ };
+
+ // println!("{:#?}", fields);
+
+ let name = &input.ident;
+
+ let to_network_impl_center = match network.repr {
+ // Repr::Aos => {}
+ Repr::Map => to_network_impl::map(&fields),
+ Repr::Maplist => to_network_impl::map_list(&fields),
+ Repr::List => to_network_impl::list(&fields),
+ _ => unimplemented!(),
+ };
+
+ let from_network_impl_center = match network.repr {
+ // Repr::Aos => {}
+ Repr::Map => from_network_impl::map(&fields),
+ Repr::Maplist => from_network_impl::map_list(&fields),
+ Repr::List => from_network_impl::list(&fields),
+ _ => unimplemented!(),
+ };
+
+ let network_impl_item = match network.repr {
+ Repr::Aos => quote! {crate::primitive::VariantList;},
+ Repr::Map => quote! {crate::primitive::VariantMap;},
+ Repr::Maplist => quote! {crate::primitive::VariantMap;},
+ Repr::List => quote! {crate::primitive::VariantList;},
+ };
+
+ // do things with `args`
+ let gen = quote! {
+ impl crate::message::signalproxy::Network for #name {
+ type Item = #network_impl_item
+
+ fn to_network(&self) -> Self::Item {
+ let mut res = Self::Item::new();
+
+ #(#to_network_impl_center)*
+
+ return res;
+ }
+
+ fn from_network(input: Self::Item) -> Self {
+ Self {
+ #(#from_network_impl_center)*
+ }
+ }
+ }
+ };
+
+ // println!("{}", gen);
+
+ gen.into()
+}
+
+fn get_field_type(field: &NetworkField) -> syn::Type {
+ match &field.override_type {
+ Some(override_type) => syn::Type::from(syn::TypePath {
+ qself: None,
+ path: syn::Path {
+ leading_colon: None,
+ segments: {
+ let mut res =
+ syn::punctuated::Punctuated::<syn::PathSegment, syn::token::Colon2>::new();
+
+ res.push(syn::PathSegment {
+ ident: syn::Ident::new(override_type, proc_macro2::Span::call_site()),
+ arguments: syn::PathArguments::None,
+ });
+
+ res
+ },
+ },
+ }),
+ None => field.ty.clone(),
+ }
+}
diff --git a/derive/src/to_network_impl.rs b/derive/src/to_network_impl.rs
new file mode 100644
index 0000000..4a8501d
--- /dev/null
+++ b/derive/src/to_network_impl.rs
@@ -0,0 +1,96 @@
+use proc_macro2::TokenStream;
+use quote::quote;
+
+use crate::{get_field_type, NetworkField};
+
+pub(crate) fn map(fields: &Vec<NetworkField>) -> Vec<TokenStream> {
+ fields
+ .iter()
+ .map(|field| {
+ let field_rename = match &field.rename {
+ Some(name) => name.clone(),
+ None => format!("{}", field.ident.as_ref().unwrap()).into(),
+ };
+
+ let field_name = field.ident.as_ref().unwrap();
+ let field_type = get_field_type(&field);
+
+ if field.to_map.is_some() {
+ let field_map: syn::ExprClosure =
+ syn::parse_str(&field.to_map.as_ref().unwrap()).unwrap();
+
+ quote! {
+ res.insert(#field_rename.to_string(),
+ crate::primitive::Variant::#field_type(self.#field_name.iter().map(#field_map).collect()));
+ }
+ } else {
+ quote! {
+ res.insert(#field_rename.to_string(),
+ crate::primitive::Variant::#field_type(self.#field_name.clone()));
+ }
+ }
+ })
+ .collect()
+}
+
+pub(crate) fn map_list(fields: &Vec<NetworkField>) -> Vec<TokenStream> {
+ fields
+ .iter()
+ .map(|field| {
+ let field_rename = match &field.rename {
+ Some(name) => name.clone(),
+ None => format!("{}", field.ident.as_ref().unwrap()).into(),
+ };
+
+ let field_name = field.ident.as_ref().unwrap();
+ let field_type = get_field_type(&field);
+
+ if field.to_map.is_some() {
+ let field_map: syn::ExprClosure =
+ syn::parse_str(&field.to_map.as_ref().unwrap()).unwrap();
+
+ quote! {
+ res.insert(#field_rename.to_string(),
+ crate::primitive::Variant::VariantList(
+ std::vec::from_elem(crate::primitive::Variant::#field_type(self.#field_name.iter().map(#field_map).collect()), 1)));
+ }
+ } else {
+ quote! {
+ res.insert(#field_rename.to_string(),
+ crate::primitive::Variant::VariantList(
+ std::vec::from_elem(crate::primitive::Variant::#field_type(self.#field_name.clone()), 1)));
+ }
+ }
+ })
+ .collect()
+}
+
+pub(crate) fn list(fields: &Vec<NetworkField>) -> Vec<TokenStream> {
+ fields
+ .iter()
+ .map(|field| {
+ let field_rename = match &field.rename {
+ Some(name) => name.clone(),
+ None => format!("{}", field.ident.as_ref().unwrap()).into(),
+ };
+
+ let field_name = field.ident.as_ref().unwrap();
+ let field_type = get_field_type(&field);
+
+ if field.to_map.is_some() {
+ let field_map: syn::ExprClosure =
+ syn::parse_str(&field.to_map.as_ref().unwrap()).unwrap();
+
+ quote! {
+ res.push(crate::primitive::Variant::ByteArray(#field_rename.to_string()));
+ res.push(crate::primitive::Variant::#field_type(self.#field_name.iter().map(#field_map).collect()));
+ }
+ } else {
+ quote! {
+ res.push(crate::primitive::Variant::ByteArray(#field_rename.to_string()));
+ res.push(crate::primitive::Variant::#field_type(self.#field_name.clone()));
+ }
+ }
+ })
+ .collect()
+}