diff options
| author | Max Audron <audron@cocaine.farm> | 2021-01-21 14:57:41 +0100 |
|---|---|---|
| committer | Max Audron <audron@cocaine.farm> | 2021-01-21 14:57:41 +0100 |
| commit | 6a6b1197f4ebc826c6b15eb3ca7f9e3efca1be19 (patch) | |
| tree | 2e6771af51fe1aec0da0e5d44b2c9ba7318451fa /derive/src | |
| parent | reorganize tests and add quassel features (diff) | |
add to and from network derive
Diffstat (limited to 'derive/src')
| -rw-r--r-- | derive/src/from_network_impl.rs | 127 | ||||
| -rw-r--r-- | derive/src/lib.rs | 144 | ||||
| -rw-r--r-- | derive/src/to_network_impl.rs | 96 |
3 files changed, 367 insertions, 0 deletions
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() +} |
