diff options
43 files changed, 1621 insertions, 1000 deletions
@@ -8,6 +8,7 @@ homepage = "https://gitlab.com/cocainefarm/libquassel" repository = "https://gitlab.com/cocainefarm/libquassel" edition = "2018" autobins = true +include = ["src/**/*", "LICENSE", "README.md"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -20,8 +21,9 @@ time = "0.2" num-traits = "0.2" num-derive = "0.3" -libquassel-derive = { path = "./derive" } +itertools = "0.10" +libquassel-derive = { path = "./derive" } bytes = { version = "1.0", optional = true } flate2 = { version = "1.0", features = ["tokio"], optional = true } diff --git a/derive/src/from/mod.rs b/derive/src/from/mod.rs new file mode 100644 index 0000000..2855d1a --- /dev/null +++ b/derive/src/from/mod.rs @@ -0,0 +1,90 @@ +use quote::quote; +use syn; +use syn::parse_macro_input; + +use darling::{FromDeriveInput, FromVariant}; + +#[derive(Debug, FromDeriveInput)] +#[darling(attributes(from), supports(enum_any))] +struct Enum { + ident: syn::Ident, + attrs: Vec<syn::Attribute>, +} + +#[derive(Debug, FromVariant)] +#[darling(attributes(from))] +struct EnumField { + ident: syn::Ident, + fields: darling::ast::Fields<syn::Type>, + + #[darling(default)] + ignore: bool, +} + +pub fn from(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as syn::DeriveInput); + // println!("{:#?}", input); + + let network = Enum::from_derive_input(&input).unwrap(); + // println!("{:#?}", network); + + let enum_name = network.ident; + + let fields: Vec<EnumField> = match &input.data { + syn::Data::Enum(data) => data + .variants + .iter() + .map(|field| EnumField::from_variant(field).expect("Could not parse field")) + .collect(), + _ => panic!("from: not an enum"), + }; + + let derives = fields + .iter() + .filter(|field| field.fields.fields.len() > 0 && !field.ignore) + .map(|field| { + let variant = &field.ident; + let inner_type = &field.fields.fields[0]; + let inner_type_str = format!("{}", quote! { #inner_type }); + + quote! { + impl From<#inner_type> for #enum_name { + fn from(input: #inner_type) -> Self { + Self::#variant(input) + } + } + + impl std::convert::TryFrom<#enum_name> for #inner_type { + type Error = String; + + fn try_from(input: #enum_name) -> Result<Self, Self::Error> { + match input { + #enum_name::#variant(input) => Ok(input), + v => Err(format!("variant::from: {} wrong variant expected: {:?}", #inner_type_str, v)), + } + } + } + + impl std::convert::TryFrom<&#enum_name> for #inner_type { + type Error = String; + + fn try_from(input: &#enum_name) -> Result<Self, Self::Error> { + match input { + #enum_name::#variant(input) => Ok(input.clone()), + v => Err(format!("variant::from: {} wrong variant expected: {:?}", #inner_type_str, v)), + } + } + } + } + }); + + // println!("{:#?}", fields); + + let gen = quote! { + #(#derives)* + }; + + // println!("{}", gen); + + gen.into() +} diff --git a/derive/src/from_network_impl.rs b/derive/src/from_network_impl.rs deleted file mode 100644 index ff05f1c..0000000 --- a/derive/src/from_network_impl.rs +++ /dev/null @@ -1,127 +0,0 @@ -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_mut(#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_mut(#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 index ddbb49a..831767a 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -1,216 +1,14 @@ -use quote::quote; use syn; -use syn::parse_macro_input; - -use darling::{FromDeriveInput, FromField, FromMeta, FromVariant}; - -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>, -} +mod from; +mod network; #[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: &mut 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(), - } -} - -#[derive(Debug, FromDeriveInput)] -#[darling(attributes(from), supports(enum_any))] -struct Enum { - ident: syn::Ident, - attrs: Vec<syn::Attribute>, -} - -#[derive(Debug, FromVariant)] -#[darling(attributes(from))] -struct EnumField { - ident: syn::Ident, - fields: darling::ast::Fields<syn::Type>, - - #[darling(default)] - ignore: bool, + network::network(input) } #[proc_macro_derive(From, attributes(from))] pub fn from(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = parse_macro_input!(input as syn::DeriveInput); - // println!("{:#?}", input); - - let network = Enum::from_derive_input(&input).unwrap(); - // println!("{:#?}", network); - - let enum_name = network.ident; - - let fields: Vec<EnumField> = match &input.data { - syn::Data::Enum(data) => data - .variants - .iter() - .map(|field| EnumField::from_variant(field).expect("Could not parse field")) - .collect(), - _ => unimplemented!(), - }; - - let derives = fields - .iter() - .filter(|field| field.fields.fields.len() > 0 && !field.ignore) - .map(|field| { - let variant = &field.ident; - let inner_type = &field.fields.fields[0]; - - quote! { - impl From<#inner_type> for #enum_name { - fn from(input: #inner_type) -> Self { - Self::#variant(input) - } - } - - impl Into<#inner_type> for #enum_name { - fn into(self) -> #inner_type { - match self { - Self::#variant(input) => input, - _ => unimplemented!(), - } - } - } - } - }); - - // println!("{:#?}", fields); - - let gen = quote! { - #(#derives)* - }; - - // println!("{}", gen); - - gen.into() + from::from(input) } diff --git a/derive/src/network/list.rs b/derive/src/network/list.rs new file mode 100644 index 0000000..75aecd0 --- /dev/null +++ b/derive/src/network/list.rs @@ -0,0 +1,83 @@ +use proc_macro2::TokenStream; +use quote::quote; + +use super::{gen_type, get_field_type, get_field_type_colon, get_field_variant_type, NetworkField}; + +pub(crate) fn to(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); + + let field_inner = if field.network { + quote! { self.#field_name.to_network() } + } else { + quote! { self.#field_name.clone() } + }; + + match &field.variant { + Some(variant_type) => { + let variant_type = gen_type(variant_type.as_str()); + quote! { + res.push(crate::primitive::Variant::ByteArray(#field_rename.to_string())); + res.push(crate::primitive::Variant::#variant_type(#field_inner)); + } + } + None => quote! { + res.push(crate::primitive::Variant::ByteArray(#field_rename.to_string())); + res.push(crate::primitive::Variant::#field_type(#field_inner)); + }, + } + }) + .collect() +} + +pub(crate) fn from(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); + let field_variant_type = get_field_variant_type(&field); + + let field_type_colon = get_field_type_colon(field_type.clone()); + + let extract_inner = quote! { + let mut i = input.iter(); + i.position(|x| *x == crate::primitive::Variant::ByteArray(String::from(#field_rename))) + .unwrap(); + + match i.next().unwrap() { + crate::primitive::Variant::#field_variant_type(input) => input.clone(), + _ => panic!("network::list::from: wrong variant type"), + } + }; + + if field.network { + quote! { + #field_name: #field_type_colon::from_network(&mut { + #extract_inner + }), + } + } else { + quote! { + #field_name: { + #extract_inner + }, + } + } + }) + .collect() +} diff --git a/derive/src/network/map.rs b/derive/src/network/map.rs new file mode 100644 index 0000000..60f96b5 --- /dev/null +++ b/derive/src/network/map.rs @@ -0,0 +1,78 @@ +use proc_macro2::{Ident, TokenStream}; +use quote::quote; + +use super::{get_field_type, get_field_type_colon, get_field_variant_type, NetworkField}; + +pub(crate) fn to(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); + let field_variant_type = get_field_variant_type(&field); + + let field_inner = if field.network { + quote! { + self.#field_name.to_network() + } + } else { + quote! { + self.#field_name.clone() + } + }; + + quote! { + res.insert(#field_rename.to_string(), + crate::primitive::Variant::#field_variant_type(#field_inner)); + } + }) + .collect() +} + +pub(crate) fn from(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); + let _field_variant_type = get_field_variant_type(&field); + + let field_type_colon = get_field_type_colon(field_type.clone()); + + if field.network { + quote! { + #field_name: #field_type_colon::from_network( + &mut std::convert::TryInto::try_into(input.remove(#field_rename).unwrap()).unwrap()), + } + } else { + quote! { + #field_name: std::convert::TryInto::try_into(input.remove(#field_rename).unwrap()).unwrap(), + } + } + }) + .collect() +} + +pub(crate) fn to_vec(_type_name: &Ident, _fields: &Vec<NetworkField>) -> TokenStream { + quote! { + self.iter().map(|item| { + item.to_network().into() + }).collect() + } +} + +pub(crate) fn from_vec(type_name: &Ident, _fields: &Vec<NetworkField>) -> TokenStream { + quote! { + input.iter().map(|item| #type_name::from_network(&mut std::convert::TryInto::try_into(item).unwrap())).collect() + } +} diff --git a/derive/src/network/maplist.rs b/derive/src/network/maplist.rs new file mode 100644 index 0000000..c92b279 --- /dev/null +++ b/derive/src/network/maplist.rs @@ -0,0 +1,167 @@ +use proc_macro2::{Ident, TokenStream}; +use quote::quote; + +use super::{get_field_type, NetworkField}; + +pub(crate) fn to(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); + + match &field.variant { + Some(variant_type) => match variant_type.as_str() { + "StringList" => quote! { + res.insert(#field_rename.to_string(), + crate::primitive::Variant::StringList( + std::vec::from_elem(self.#field_name.clone(), 1))); + }, + _ => panic!("network::map::to: not one of the avaible variants") + } + None => 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 to_vec(_type_name: &Ident, fields: &Vec<NetworkField>) -> TokenStream { + let (lists, for_each_inner, map_inserts): ( + Vec<TokenStream>, + Vec<TokenStream>, + Vec<TokenStream>, + ) = fields.iter().fold( + (Vec::new(), Vec::new(), Vec::new()), + |(mut lists, mut for_each_inner, mut map_inserts), 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); + + match &field.variant { + None => { + lists.push(quote! { + let mut #field_name: crate::primitive::VariantList = Vec::with_capacity(self.len()); + }); + + for_each_inner.push(quote! { + #field_name.push(crate::primitive::Variant::#field_type(item.#field_name.clone())); + }); + + map_inserts.push(quote! { + map.insert(String::from(#field_rename), crate::primitive::Variant::VariantList(#field_name)); + }); + } + Some(variant_type) => match variant_type.as_str() { + "StringList" => { + lists.push(quote! { + let mut #field_name: crate::primitive::StringList = Vec::with_capacity(self.len()); + }); + + for_each_inner.push(quote! { + #field_name.push(item.#field_name.clone()); + }); + + map_inserts.push(quote! { + map.insert(String::from(#field_rename), crate::primitive::Variant::StringList(#field_name)); + }); + } + _ => panic!("network::map::to: not one of the avaible variants") + } + } + + return (lists, for_each_inner, map_inserts); + }, + ); + + quote! { + #(#lists)* + + let mut map = crate::primitive::VariantMap::new(); + + self.iter().for_each(|item| { + #(#for_each_inner)* + }); + + #(#map_inserts)* + + return map; + } +} + +pub(crate) fn from(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); + + match &field.variant { + None => quote! { + #field_name: match input.get_mut(#field_rename).unwrap() { + crate::primitive::Variant::VariantList(input) => std::convert::TryInto::try_into(input.remove(0)).unwrap(), + _ => panic!("#field_name: wrong variant") + }, + }, + Some(variant_type) => match variant_type.as_str() { + "StringList" => quote! { + #field_name: match input.get_mut(#field_rename).unwrap() { + crate::primitive::Variant::StringList(input) => input.remove(0), + _ => panic!("#field_name: wrong variant") + }, + }, + _ => panic!("network::map::to: not one of the avaible variants"), + } + } + }) + .collect() +} + +pub(crate) fn from_vec(type_name: &Ident, fields: &Vec<NetworkField>) -> TokenStream { + let field = &fields[0]; + + 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_variant = match &field.variant { + None => quote! {crate::primitive::VariantList}, + Some(variant_type) => match variant_type.as_str() { + "StringList" => quote! {crate::primitive::StringList}, + "VariantMap" => quote! {crate::primitive::VariantMap}, + _ => panic!("network::map::from_vec: not one of the avaible variants"), + }, + }; + + quote! { + let marker: #field_variant = std::convert::TryInto::try_into(input.get(#field_rename).unwrap()).unwrap(); + + let mut res = Vec::new(); + for _ in 0..marker.len() { + res.push(#type_name::from_network(input)); + } + + return res; + } +} diff --git a/derive/src/network/mod.rs b/derive/src/network/mod.rs new file mode 100644 index 0000000..f39bedd --- /dev/null +++ b/derive/src/network/mod.rs @@ -0,0 +1,230 @@ +use quote::quote; +use syn::{self, parse_macro_input}; + +use darling::{FromDeriveInput, FromField, FromMeta}; + +mod list; +mod map; +mod maplist; + +#[derive(Debug, FromDeriveInput)] +#[darling(attributes(network), supports(struct_any))] +/// Derive to and from network methods for quassel objects +pub struct Network { + ident: syn::Ident, + attrs: Vec<syn::Attribute>, + /// Representation to choose for the network format + /// see Repr enum + repr: Repr, +} + +#[derive(Debug, Clone, Copy, FromMeta)] +#[darling(default)] +pub enum Repr { + List, + Map, + Maplist, +} + +impl Default for Repr { + fn default() -> Self { + Repr::List + } +} + +// #[derive(Debug, Clone, Copy, FromMeta)] +// #[darling(default)] +// pub enum Variant { +// Variantlist, +// Stringlist, +// Variantmap, +// Default, +// } + +// impl Default for Variant { +// fn default() -> Self { +// Self::Default +// } +// } + +#[derive(Debug, FromField)] +#[darling(attributes(network))] +pub struct NetworkField { + ident: Option<syn::Ident>, + ty: syn::Type, + + #[darling(default)] + rename: Option<String>, + #[darling(default)] + override_type: Option<String>, + /// Variant to encapsulate this field + /// VariantList (default) or StringList + #[darling(default)] + variant: Option<String>, + /// field is a nested network repr so + /// use to_network and from_network on it + #[darling(default)] + network: bool, +} + +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(), + _ => panic!("network: not a named field"), + }, + _ => panic!("network: not a Struct"), + }; + + // println!("{:#?}", fields); + + let name = &input.ident; + + let to_network_impl_center = match network.repr { + // Repr::Aos => {} + Repr::Map => map::to(&fields), + Repr::Maplist => maplist::to(&fields), + Repr::List => list::to(&fields), + }; + + let from_network_impl_center = match network.repr { + // Repr::Aos => {} + Repr::Map => map::from(&fields), + Repr::Maplist => maplist::from(&fields), + Repr::List => list::from(&fields), + }; + + let network_impl_item = match network.repr { + Repr::Map => quote! {crate::primitive::VariantMap;}, + Repr::Maplist => quote! {crate::primitive::VariantMap;}, + Repr::List => quote! {crate::primitive::VariantList;}, + }; + + let mut 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: &mut Self::Item) -> Self { + Self { + #(#from_network_impl_center)* + } + } + } + }; + + if let Repr::Maplist | Repr::Map = network.repr { + let network_impl_item_vec = match network.repr { + Repr::Map => quote! {crate::primitive::VariantList;}, + Repr::Maplist => quote! {crate::primitive::VariantMap;}, + Repr::List => quote! {crate::primitive::VariantList;}, + }; + + let to_network_impl_vec_center = match network.repr { + Repr::Maplist => maplist::to_vec(name, &fields), + Repr::Map => map::to_vec(name, &fields), + _ => unimplemented!(), + }; + + let from_network_impl_vec_center = match network.repr { + Repr::Maplist => maplist::from_vec(name, &fields), + Repr::Map => map::from_vec(name, &fields), + _ => unimplemented!(), + }; + + let vec = quote! { + impl crate::message::signalproxy::Network for Vec<#name> { + type Item = #network_impl_item_vec + + fn to_network(&self) -> Self::Item { + #to_network_impl_vec_center + } + + fn from_network(input: &mut Self::Item) -> Self { + #from_network_impl_vec_center + } + } + }; + + gen.extend(vec); + } + + // println!("{}", gen); + + gen.into() +} + +fn get_field_type(field: &NetworkField) -> syn::Type { + if let Some(override_type) = &field.override_type { + gen_type(override_type) + } else { + field.ty.clone() + } +} + +fn get_field_variant_type(field: &NetworkField) -> syn::Type { + match &field.variant { + Some(ty) => gen_type(&ty), + None => { + if let Some(override_type) = &field.override_type { + gen_type(override_type) + } else { + field.ty.clone() + } + } + } +} + +fn gen_type(typ: &str) -> syn::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(typ, proc_macro2::Span::call_site()), + arguments: syn::PathArguments::None, + }); + + res + }, + }, + }) +} + +fn get_field_type_colon(mut ty: syn::Type) -> syn::Type { + match &mut ty { + syn::Type::Path(path) => { + let first_seg = path.path.segments.first_mut().unwrap(); + match &mut first_seg.arguments { + syn::PathArguments::AngleBracketed(bracket) => { + bracket.colon2_token = Some(syn::parse_str("::").unwrap()); + } + _ => (), + } + } + _ => (), + }; + + return ty; +} diff --git a/derive/src/to_network_impl.rs b/derive/src/to_network_impl.rs deleted file mode 100644 index 4a8501d..0000000 --- a/derive/src/to_network_impl.rs +++ /dev/null @@ -1,96 +0,0 @@ -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() -} diff --git a/examples/quasselproxy/src/main.rs b/examples/quasselproxy/src/main.rs index 16c0fa9..227240a 100644 --- a/examples/quasselproxy/src/main.rs +++ b/examples/quasselproxy/src/main.rs @@ -148,7 +148,7 @@ impl Server { tls: bool, compression: bool, ) -> Result<ConnAck, Error> { - use libquassel::Deserialize; + use libquassel::deserialize::*; // Buffer for our initialization let mut init: Vec<u8> = vec![]; @@ -217,7 +217,7 @@ impl Server { ) -> Result<(), Error> { use libquassel::HandshakeDeserialize; - trace!(target: "message", "Received bytes: {:x?}", buf); + trace!(target: "handshakemessage", "Received bytes: {:x?}", buf); match HandshakeMessage::parse(buf) { Ok((_size, res)) => { info!("{}: {:#?}", direction, res); @@ -236,8 +236,8 @@ impl Server { #[tracing::instrument] async fn handle_message(buf: &[u8], direction: &str) -> Result<(), Error> { + use libquassel::deserialize::*; use libquassel::message::Message; - use libquassel::Deserialize; trace!(target: "message", "Received bytes: {:x?}", buf); @@ -1,3 +1,4 @@ +#![feature(test)] #![feature(external_doc)] #![feature(doc_cfg)] #![doc(include = "../README.md")] @@ -26,32 +27,51 @@ pub mod error; /// Framing impl to be used with [`tokio_util::codec::Framed`] pub mod frame; -use failure::Error; +/// Traits for Serialization of objects +pub mod serialize { + use failure::Error; -/// Serialization of types and structs to the quassel byteprotocol -pub trait Serialize { - fn serialize(&self) -> Result<Vec<u8>, Error>; -} + /// Serialization of types and structs to the quassel byteprotocol + pub trait Serialize { + fn serialize(&self) -> Result<Vec<u8>, Error>; + } -/// Serialization of UTF-8 based Strings to the quassel byteprotocol -pub trait SerializeUTF8 { - fn serialize_utf8(&self) -> Result<Vec<u8>, Error>; -} + /// Serialization of UTF-8 based Strings to the quassel byteprotocol + pub trait SerializeUTF8 { + fn serialize_utf8(&self) -> Result<Vec<u8>, Error>; + } -/// Deserialization of types and structs to the quassel byteprotocol -pub trait Deserialize { - fn parse(b: &[u8]) -> Result<(usize, Self), Error> - where - Self: std::marker::Sized; + pub trait SerializeVariant { + fn serialize_variant(&self) -> Result<Vec<u8>, Error>; + } } -/// Deserialization of UTF-8 based Strings to the quassel byteprotocol -pub trait DeserializeUTF8 { - fn parse_utf8(b: &[u8]) -> Result<(usize, Self), Error> - where - Self: std::marker::Sized; +/// Traits for parsing objects +pub mod deserialize { + use failure::Error; + + /// Deserialization of types and structs to the quassel byteprotocol + pub trait Deserialize { + fn parse(b: &[u8]) -> Result<(usize, Self), Error> + where + Self: std::marker::Sized; + } + + /// Deserialization of UTF-8 based Strings to the quassel byteprotocol + pub trait DeserializeUTF8 { + fn parse_utf8(b: &[u8]) -> Result<(usize, Self), Error> + where + Self: std::marker::Sized; + } + + pub trait DeserializeVariant { + fn parse_variant(b: &[u8]) -> Result<(usize, Self), Error> + where + Self: std::marker::Sized; + } } +use failure::Error; /// HandshakeSerialize implements the serialization needed during the handhake phase. /// /// The protocol has some minor differences during this phase compared to the regular parsing. diff --git a/src/message/handshake/connack.rs b/src/message/handshake/connack.rs index 222c08c..a246679 100644 --- a/src/message/handshake/connack.rs +++ b/src/message/handshake/connack.rs @@ -29,7 +29,7 @@ impl Default for ConnAck { } } -impl crate::Serialize for ConnAck { +impl crate::serialize::Serialize for ConnAck { fn serialize(&self) -> Result<Vec<std::primitive::u8>, Error> { let mut bytes: Vec<u8> = Vec::new(); @@ -41,7 +41,7 @@ impl crate::Serialize for ConnAck { } } -impl crate::Deserialize for ConnAck { +impl crate::deserialize::Deserialize for ConnAck { fn parse(b: &[u8]) -> Result<(usize, Self), Error> { let (flen, flags) = u8::parse(b)?; let (elen, extra) = i16::parse(&b[flen..])?; diff --git a/src/message/handshake/init.rs b/src/message/handshake/init.rs index b4604b6..df1b29e 100644 --- a/src/message/handshake/init.rs +++ b/src/message/handshake/init.rs @@ -1,5 +1,4 @@ -use crate::Deserialize; -use crate::Serialize; +use crate::{deserialize::Deserialize, serialize::Serialize}; /// The first few bytes sent to the core to initialize the connection and setup if we want to use tls and compression #[derive(Clone, Debug)] diff --git a/src/message/handshake/mod.rs b/src/message/handshake/mod.rs index c180c5e..029eb86 100644 --- a/src/message/handshake/mod.rs +++ b/src/message/handshake/mod.rs @@ -24,7 +24,7 @@ pub use protocol::*; pub use sessioninit::*; pub use types::*; -use crate::primitive::{Variant, VariantMap}; +use crate::primitive::VariantMap; use crate::{HandshakeDeserialize, HandshakeSerialize}; #[derive(Debug, Clone)] @@ -56,7 +56,7 @@ impl HandshakeDeserialize for HandshakeMessage { fn parse(b: &[u8]) -> Result<(usize, Self), failure::Error> { let (size, res) = VariantMap::parse(b)?; - let msgtype = match_variant!(&res["MsgType"], Variant::String); + let msgtype: String = (&res["MsgType"]).into(); match msgtype.as_str() { "ClientInit" => Ok((size, HandshakeMessage::ClientInit(res.into()))), "ClientInitAck" => Ok((size, HandshakeMessage::ClientInitAck(res.into()))), diff --git a/src/message/handshake/protocol.rs b/src/message/handshake/protocol.rs index d020f33..c12da05 100644 --- a/src/message/handshake/protocol.rs +++ b/src/message/handshake/protocol.rs @@ -1,5 +1,4 @@ -use crate::Deserialize; -use crate::Serialize; +use crate::{deserialize::Deserialize, serialize::Serialize}; pub enum Protocol { Legacy = 0x00000001, diff --git a/src/message/handshake/sessioninit.rs b/src/message/handshake/sessioninit.rs index d1b4b90..a663cce 100644 --- a/src/message/handshake/sessioninit.rs +++ b/src/message/handshake/sessioninit.rs @@ -1,3 +1,5 @@ +use std::convert::TryInto; + use crate::message::objects::Identity; use crate::primitive::{BufferInfo, Variant, VariantMap}; use crate::HandshakeSerialize; @@ -18,12 +20,15 @@ pub struct SessionInit { impl From<VariantMap> for SessionInit { fn from(input: VariantMap) -> Self { - let state = match_variant!(input.get("SessionState").unwrap(), Variant::VariantMap); + use crate::message::signalproxy::Network; + let state: VariantMap = input.get("SessionState").unwrap().try_into().unwrap(); + + log::trace!("sessionstate: {:#?}", state); + SessionInit { - identities: match_variant!(state.get("Identities").unwrap(), Variant::VariantList) - .iter() - .map(|ident| Identity::from(match_variant!(ident, Variant::VariantMap))) - .collect(), + identities: Vec::<Identity>::from_network( + &mut state.get("Identities").unwrap().try_into().unwrap(), + ), buffers: match_variant!(state.get("BufferInfos").unwrap(), Variant::VariantList) .iter() .map(|buffer| match buffer { @@ -49,15 +54,15 @@ impl HandshakeSerialize for SessionInit { "MsgType".to_string(), Variant::String("SessionInit".to_string()), ); - values.insert( - "Identities".to_string(), - Variant::VariantList( - self.identities - .iter() - .map(|ident| Variant::VariantMap(ident.clone().into())) - .collect(), - ), - ); + // values.insert( + // "Identities".to_string(), + // Variant::VariantList( + // self.identities + // .iter() + // .map(|ident| Variant::VariantMap(ident.clone().into())) + // .collect(), + // ), + // ); values.insert( "BufferInfos".to_string(), Variant::VariantList( diff --git a/src/message/handshake/types.rs b/src/message/handshake/types.rs index 24d847b..e9e6469 100644 --- a/src/message/handshake/types.rs +++ b/src/message/handshake/types.rs @@ -7,11 +7,10 @@ use failure::Error; use crate::error::ProtocolError; use crate::primitive::Variant; use crate::util; -use crate::Deserialize; -use crate::Serialize; +use crate::{deserialize::Deserialize, serialize::Serialize}; +use crate::message::handshake::{HandshakeDeserialize, HandshakeSerialize}; use crate::primitive::VariantMap; -use crate::{HandshakeDeserialize, HandshakeSerialize}; impl HandshakeSerialize for VariantMap { fn serialize<'a>(&'a self) -> Result<Vec<u8>, Error> { diff --git a/src/message/signalproxy/heartbeat.rs b/src/message/signalproxy/heartbeat.rs index 46bfd51..58dc430 100644 --- a/src/message/signalproxy/heartbeat.rs +++ b/src/message/signalproxy/heartbeat.rs @@ -1,6 +1,6 @@ use crate::message::MessageType; use crate::primitive::{DateTime, Variant, VariantList}; -use crate::{Deserialize, Serialize}; +use crate::{deserialize::Deserialize, serialize::Serialize}; #[derive(Clone, Debug, std::cmp::PartialEq)] pub struct HeartBeat { diff --git a/src/message/signalproxy/initdata.rs b/src/message/signalproxy/initdata.rs index abeacad..7c3e443 100644 --- a/src/message/signalproxy/initdata.rs +++ b/src/message/signalproxy/initdata.rs @@ -1,12 +1,14 @@ use crate::message::MessageType; use crate::primitive::{Variant, VariantList}; -use crate::{Deserialize, Serialize}; +use crate::{deserialize::Deserialize, serialize::Serialize}; + +use super::objects::Types; #[derive(Clone, Debug, std::cmp::PartialEq)] pub struct InitData { class_name: String, object_name: String, - init_data: VariantList, + init_data: Types, } impl Serialize for InitData { @@ -17,24 +19,26 @@ impl Serialize for InitData { res.push(Variant::ByteArray(self.class_name.clone())); res.push(Variant::ByteArray(self.object_name.clone())); - res.append(&mut self.init_data.clone()); + res.append(&mut self.init_data.to_network()); res.serialize() } } impl Deserialize for InitData { - fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> { + fn parse(b: &[u8]) -> Result<(usize, Self), failure::Error> { let (size, mut res) = VariantList::parse(&b)?; res.remove(0); + let class_name: String = res.remove(0).into(); + Ok(( size, Self { - class_name: match_variant!(res.remove(0), Variant::ByteArray), - object_name: match_variant!(res.remove(0), Variant::ByteArray), - init_data: res, + class_name: class_name.clone(), + object_name: res.remove(0).into(), + init_data: Types::from_network(class_name.as_str(), &mut res), }, )) } diff --git a/src/message/signalproxy/initrequest.rs b/src/message/signalproxy/initrequest.rs index 5190e0a..1beef5c 100644 --- a/src/message/signalproxy/initrequest.rs +++ b/src/message/signalproxy/initrequest.rs @@ -1,6 +1,6 @@ use crate::message::MessageType; use crate::primitive::{Variant, VariantList}; -use crate::{Deserialize, Serialize}; +use crate::{deserialize::Deserialize, serialize::Serialize}; #[derive(Clone, Debug, std::cmp::PartialEq)] pub struct InitRequest { diff --git a/src/message/signalproxy/mod.rs b/src/message/signalproxy/mod.rs index 9887af7..2887407 100644 --- a/src/message/signalproxy/mod.rs +++ b/src/message/signalproxy/mod.rs @@ -1,4 +1,6 @@ -use crate::{Deserialize, Serialize}; +use crate::{deserialize::Deserialize, serialize::Serialize}; + +use num_derive::{FromPrimitive, ToPrimitive}; mod heartbeat; mod initdata; @@ -98,7 +100,7 @@ impl Deserialize for Message { /// Type of an SignalProxy Message /// The first element in the VariantList that is received #[repr(i32)] -#[derive(Copy, Clone, Debug, std::cmp::PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, FromPrimitive, ToPrimitive)] pub enum MessageType { /// Bidirectional SyncMessage = 0x00000001, diff --git a/src/message/signalproxy/objects/aliasmanager.rs b/src/message/signalproxy/objects/aliasmanager.rs index 093163b..b536cde 100644 --- a/src/message/signalproxy/objects/aliasmanager.rs +++ b/src/message/signalproxy/objects/aliasmanager.rs @@ -1,93 +1,27 @@ -use crate::primitive::{StringList, Variant, VariantMap}; +use libquassel_derive::Network; -use crate::message::signalproxy::Network; - -#[derive(Clone, Debug, std::cmp::PartialEq)] +#[derive(Clone, Debug, std::cmp::PartialEq, Network)] +#[network(repr = "list")] pub struct AliasManager { + #[network(rename = "Aliases", variant = "VariantMap", network)] pub aliases: Vec<Alias>, } -#[derive(Clone, Debug, std::cmp::PartialEq)] +#[derive(Clone, Debug, std::cmp::PartialEq, Network)] +#[network(repr = "maplist")] pub struct Alias { + #[network(rename = "names", variant = "StringList")] name: String, + #[network(rename = "expansions", variant = "StringList")] expansion: String, } -impl Alias { - fn from_network_internal(input: &VariantMap) -> Vec<Self> { - let names = match_variant!(input.get("names").unwrap(), Variant::StringList); - let expansions = match_variant!(input.get("expansions").unwrap(), Variant::StringList); - - return names - .iter() - .zip(expansions) - .map(|(name, expansion)| Alias { - name: name.clone(), - expansion, - }) - .collect(); - } -} - -impl Network for Alias { - type Item = VariantMap; - - fn to_network(&self) -> Self::Item { - let mut map = VariantMap::new(); - map.insert(s!("names"), Variant::StringList(vec![self.name.clone()])); - map.insert( - s!("expansions"), - Variant::StringList(vec![self.expansion.clone()]), - ); - - return map; - } - - fn from_network(input: &mut Self::Item) -> Self { - Alias::from_network_internal(&input)[0].clone() - } -} - -impl Network for AliasManager { - type Item = VariantMap; - - fn to_network(&self) -> Self::Item { - let (names, expansions) = self.aliases.iter().fold( - (StringList::new(), StringList::new()), - |(mut names, mut expansions), alias| { - names.push(alias.name.clone()); - expansions.push(alias.expansion.clone()); - return (names, expansions); - }, - ); - - let mut map = VariantMap::new(); - map.insert(s!("names"), Variant::StringList(names)); - map.insert(s!("expansions"), Variant::StringList(expansions)); - - return map; - } - - fn from_network(input: &mut Self::Item) -> Self { - let names = match_variant!(input.get("names").unwrap(), Variant::StringList); - let expansions = match_variant!(input.get("expansions").unwrap(), Variant::StringList); - - AliasManager { - aliases: names - .iter() - .zip(expansions) - .map(|(name, expansion)| Alias { - name: name.clone(), - expansion, - }) - .collect(), - } - } -} - #[cfg(test)] mod tests { use super::*; + use crate::message::signalproxy::translation::Network; + + use crate::primitive::{Variant, VariantList}; fn get_src() -> AliasManager { AliasManager { @@ -104,23 +38,36 @@ mod tests { } } - fn get_dest() -> VariantMap { - map! { - s!("names") => Variant::StringList( - vec![ - s!("j"), - s!("ns"), - ], - ), - s!("expansions") => Variant::StringList( - vec![ - s!("/join $0"), - s!("/msg nickserv $0"), - ], - ), - } + fn get_dest() -> VariantList { + vec![ + Variant::ByteArray(s!("Aliases")), + Variant::VariantMap(map! { + s!("names") => Variant::StringList( + vec![ + s!("j"), + s!("ns"), + ], + ), + s!("expansions") => Variant::StringList( + vec![ + s!("/join $0"), + s!("/msg nickserv $0"), + ], + ), + }), + ] } + // #[bench] + // fn alias_to_network(b: &mut test::Bencher) { + // b.iter(|| test::black_box(get_src()).to_network()) + // } + + // #[bench] + // fn alias_from_network(b: &mut test::Bencher) { + // b.iter(|| AliasManager::from_network(&mut test::black_box(get_dest()))) + // } + #[test] fn aliasmanager_to_network() { assert_eq!(get_src().to_network(), get_dest()) diff --git a/src/message/signalproxy/objects/buffersyncer.rs b/src/message/signalproxy/objects/buffersyncer.rs index 7ee4ca5..cc26310 100644 --- a/src/message/signalproxy/objects/buffersyncer.rs +++ b/src/message/signalproxy/objects/buffersyncer.rs @@ -1,13 +1,148 @@ -use crate::message::signalproxy::MessageType; -use std::collections::HashMap; +use std::{collections::HashMap, convert::TryInto}; -// use default_macro::default; -// #[default(crate::message::signalproxy::objects::BufferSyncerClient)] +use num_traits::{FromPrimitive, ToPrimitive}; + +use itertools::Itertools; + +use crate::{ + message::signalproxy::Network, + primitive::{MessageType, Variant, VariantList}, +}; + +#[derive(Debug, Clone, PartialEq)] pub struct BufferSyncer { - pub activities: HashMap<u32, MessageType>, - pub highlight_counts: HashMap<u32, u32>, - pub last_seen_msg: HashMap<u32, u32>, - pub marker_line: HashMap<u32, u32>, + pub activities: HashMap<i32, MessageType>, + pub highlight_counts: HashMap<i32, i32>, + pub last_seen_msg: HashMap<i32, i64>, + pub marker_line: HashMap<i32, i64>, +} + +impl Network for BufferSyncer { + type Item = VariantList; + + fn to_network(&self) -> Self::Item { + let mut res = Self::Item::new(); + + res.push(Variant::ByteArray(s!("Activities"))); + res.push(Variant::VariantList({ + let mut res = VariantList::new(); + + self.activities.iter().for_each(|(k, v)| { + res.push(Variant::i32(*k)); + res.push(Variant::i32(v.to_i32().unwrap())); + }); + + res + })); + + res.push(Variant::ByteArray(s!("HighlightCounts"))); + res.push(Variant::VariantList({ + let mut res = VariantList::new(); + + self.highlight_counts.iter().for_each(|(k, v)| { + res.push(Variant::i32(*k)); + res.push(Variant::i32(*v)); + }); + + res + })); + + res.push(Variant::ByteArray(s!("LastSeenMsg"))); + res.push(Variant::VariantList({ + let mut res = VariantList::new(); + + self.last_seen_msg.iter().for_each(|(k, v)| { + res.push(Variant::i32(*k)); + res.push(Variant::i64(*v)); + }); + + res + })); + + res.push(Variant::ByteArray(s!("MarkerLines"))); + res.push(Variant::VariantList({ + let mut res = VariantList::new(); + + self.marker_line.iter().for_each(|(k, v)| { + res.push(Variant::i32(*k)); + res.push(Variant::i64(*v)); + }); + + res + })); + + res + } + + fn from_network(input: &mut Self::Item) -> Self { + let mut i = input.iter().cycle(); + + i.position(|x| *x == crate::primitive::Variant::ByteArray(s!("Activities"))) + .unwrap(); + let activities: VariantList = i.next().unwrap().try_into().unwrap(); + let activities = activities + .iter() + .batching(|it| match it.next() { + None => None, + Some(x) => match it.next() { + None => None, + Some(y) => Some(( + x.try_into().unwrap(), + MessageType::from_i32(y.try_into().unwrap()).unwrap(), + )), + }, + }) + .collect(); + + i.position(|x| *x == crate::primitive::Variant::ByteArray(s!("HighlightCounts"))) + .unwrap(); + let highlight_counts: VariantList = i.next().unwrap().try_into().unwrap(); + let highlight_counts = highlight_counts + .iter() + .batching(|it| match it.next() { + None => None, + Some(x) => match it.next() { + None => None, + Some(y) => Some((x.try_into().unwrap(), y.try_into().unwrap())), + }, + }) + .collect(); + + i.position(|x| *x == crate::primitive::Variant::ByteArray(s!("LastSeenMsg"))) + .unwrap(); + let last_seen_msg: VariantList = i.next().unwrap().try_into().unwrap(); + let last_seen_msg = last_seen_msg + .iter() + .batching(|it| match it.next() { + None => None, + Some(x) => match it.next() { + None => None, + Some(y) => Some((x.try_into().unwrap(), y.try_into().unwrap())), + }, + }) + .collect(); + + i.position(|x| *x == crate::primitive::Variant::ByteArray(s!("MarkerLines"))) + .unwrap(); + let marker_line: VariantList = i.next().unwrap().try_into().unwrap(); + let marker_line = marker_line + .iter() + .batching(|it| match it.next() { + None => None, + Some(x) => match it.next() { + None => None, + Some(y) => Some((x.try_into().unwrap(), y.try_into().unwrap())), + }, + }) + .collect(); + + Self { + activities, + highlight_counts, + last_seen_msg, + marker_line, + } + } } pub trait BufferSyncerServer { @@ -72,3 +207,115 @@ pub trait BufferSyncerClient { self.marker_line_mut().insert(buffer, msgid); } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::message::signalproxy::translation::Network; + use crate::primitive::{Variant, VariantList}; + use pretty_assertions::assert_eq; + + fn get_network() -> VariantList { + vec![ + Variant::ByteArray(s!("Activities")), + Variant::VariantList(vec![ + Variant::i32(1), + Variant::i32(0), + Variant::i32(2), + Variant::i32(0), + Variant::i32(3), + Variant::i32(0), + Variant::i32(4), + Variant::i32(0), + Variant::i32(5), + Variant::i32(0), + ]), + Variant::ByteArray(s!("HighlightCounts")), + Variant::VariantList(vec![ + Variant::i32(1), + Variant::i32(0), + Variant::i32(2), + Variant::i32(0), + Variant::i32(3), + Variant::i32(0), + Variant::i32(4), + Variant::i32(0), + Variant::i32(5), + Variant::i32(0), + ]), + Variant::ByteArray(s!("LastSeenMsg")), + Variant::VariantList(vec![ + Variant::i32(1), + Variant::i64(2185), + Variant::i32(2), + Variant::i64(2188), + Variant::i32(3), + Variant::i64(860), + Variant::i32(4), + Variant::i64(2183), + Variant::i32(5), + Variant::i64(2180), + ]), + Variant::ByteArray(s!("MarkerLines")), + Variant::VariantList(vec![ + Variant::i32(1), + Variant::i64(2185), + Variant::i32(2), + Variant::i64(2188), + Variant::i32(3), + Variant::i64(860), + Variant::i32(4), + Variant::i64(1527), + Variant::i32(5), + Variant::i64(2180), + ]), + ] + } + + fn get_runtime() -> BufferSyncer { + BufferSyncer { + activities: map! { + 1 => MessageType::None, + 2 => MessageType::None, + 3 => MessageType::None, + 4 => MessageType::None, + 5 => MessageType::None, + }, + highlight_counts: map! { + 1 => 0, + 2 => 0, + 3 => 0, + 4 => 0, + 5 => 0, + }, + last_seen_msg: map! { + 1 => 2185, + 2 => 2188, + 3 => 860, + 4 => 2183, + 5 => 2180, + }, + marker_line: map! { + 1 => 2185, + 2 => 2188, + 3 => 860, + 4 => 1527, + 5 => 2180, + }, + } + } + + // Disabled cus not sorted + // #[test] + // fn buffersyncer_to_network() { + // assert_eq!(get_runtime().to_network(), get_network()) + // } + + #[test] + fn buffersyncer_from_network() { + assert_eq!( + BufferSyncer::from_network(&mut get_network()), + get_runtime() + ) + } +} diff --git a/src/message/signalproxy/objects/coreinfo.rs b/src/message/signalproxy/objects/coreinfo.rs new file mode 100644 index 0000000..a717366 --- /dev/null +++ b/src/message/signalproxy/objects/coreinfo.rs @@ -0,0 +1,60 @@ +use libquassel_derive::Network; + +use crate::primitive::{DateTime, StringList}; + +#[derive(Debug, Clone, PartialEq, Network)] +#[network(repr = "map")] +pub struct CoreInfo { + #[network(rename = "coreData", network, variant = "VariantMap")] + core_data: CoreData, +} + +// // S->C calls +// setCoreData(coreData: QVariantMap) +// /** +// * Replaces all properties of the object with the content of the +// * "properties" parameter. This parameter is in network representation. +// */ +// update(properties: QVariantMap) + +#[derive(Debug, Clone, PartialEq, Network)] +#[network(repr = "map")] +pub struct CoreData { + #[network(rename = "quasselVersion")] + quassel_version: String, + #[network(rename = "quasselBuildDate")] + quassel_build_date: String, + #[network(rename = "startTime")] + start_time: DateTime, + #[network(rename = "sessionConnectedClients")] + session_connected_clients: i32, + #[network( + rename = "sessionConnectedClientData", + variant = "VariantList", + network + )] + session_connected_client_data: Vec<ConnectedClient>, +} + +#[derive(Debug, Clone, PartialEq, Network)] +#[network(repr = "map")] +pub struct ConnectedClient { + #[network(rename = "id")] + id: i32, + #[network(rename = "remoteAddress")] + remote_address: String, + #[network(rename = "location")] + location: String, + #[network(rename = "clientVersion")] + client_version: String, + #[network(rename = "clientVersionDate")] + client_version_date: String, + #[network(rename = "connectedSince")] + connected_since: DateTime, + #[network(rename = "secure")] + secure: bool, + #[network(rename = "features")] + features: i32, + #[network(rename = "featureList")] + feature_list: StringList, +} diff --git a/src/message/signalproxy/objects/highlightrulemanager.rs b/src/message/signalproxy/objects/highlightrulemanager.rs new file mode 100644 index 0000000..d579254 --- /dev/null +++ b/src/message/signalproxy/objects/highlightrulemanager.rs @@ -0,0 +1,113 @@ +use libquassel_derive::Network; + +use num_derive::{FromPrimitive, ToPrimitive}; +use num_traits::{FromPrimitive, ToPrimitive}; + +#[derive(Debug, Clone, PartialEq, Network)] +#[network(repr = "list")] +pub struct HighlightRuleManager { + #[network(rename = "highlightRuleList", network, variant = "VariantMap")] + highlight_rule_list: Vec<HighlightRule>, + #[network(rename = "highlightNick", network, variant = "i32")] + highlight_nick: HighlightNickType, + #[network(rename = "nicksCaseSensitive")] + nicks_case_sensitive: bool, +} + +#[derive(Debug, Clone, PartialEq, Network)] +#[network(repr = "maplist")] +pub struct HighlightRule { + id: i32, + #[network(variant = "StringList")] + name: String, + #[network(rename = "isRegEx")] + is_regex: bool, + #[network(rename = "isCaseSensitive")] + is_case_sensitive: bool, + #[network(rename = "isEnabled")] + is_enabled: bool, + #[network(rename = "isInverse")] + is_inverse: bool, + #[network(variant = "StringList")] + sender: String, + #[network(variant = "StringList")] + channel: String, +} + +#[derive(Debug, Clone, PartialEq, FromPrimitive, ToPrimitive)] +pub enum HighlightNickType { + NoNick = 0x00, + CurrentNick = 0x01, + AllNicks = 0x02, +} + +impl crate::message::signalproxy::Network for HighlightNickType { + type Item = i32; + + fn to_network(&self) -> Self::Item { + self.to_i32().unwrap() + } + + fn from_network(input: &mut Self::Item) -> Self { + Self::from_i32(*input).unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::message::signalproxy::translation::Network; + use crate::primitive::{Variant, VariantList}; + + use pretty_assertions::assert_eq; + + fn get_network() -> VariantList { + vec![ + Variant::ByteArray(s!("highlightRuleList")), + Variant::VariantMap(map! { + s!("isInverse") => Variant::VariantList(vec![Variant::bool(false)]), + s!("isEnabled") => Variant::VariantList(vec![Variant::bool(true)]), + s!("channel") => Variant::StringList(vec![s!("#test")]), + s!("sender") => Variant::StringList(vec![s!("testuser")]), + s!("isCaseSensitive") => Variant::VariantList(vec![Variant::bool(false)]), + s!("isRegEx") => Variant::VariantList(vec![Variant::bool(false)]), + s!("name") => Variant::StringList(vec![s!("testrule")]), + s!("id") => Variant::VariantList(vec![Variant::i32(1)]), + }), + Variant::ByteArray(s!("highlightNick")), + Variant::i32(1), + Variant::ByteArray(s!("nicksCaseSensitive")), + Variant::bool(false), + ] + } + + fn get_runtime() -> HighlightRuleManager { + HighlightRuleManager { + highlight_rule_list: vec![HighlightRule { + id: 1, + name: s!("testrule"), + is_regex: false, + is_case_sensitive: false, + is_enabled: true, + is_inverse: false, + sender: s!("testuser"), + channel: s!("#test"), + }], + highlight_nick: HighlightNickType::CurrentNick, + nicks_case_sensitive: false, + } + } + + #[test] + fn highlightrulemanager_to_network() { + assert_eq!(get_runtime().to_network(), get_network()) + } + + #[test] + fn highlightrulemanager_from_network() { + assert_eq!( + HighlightRuleManager::from_network(&mut get_network()), + get_runtime() + ) + } +} diff --git a/src/message/signalproxy/objects/identity.rs b/src/message/signalproxy/objects/identity.rs index a710e5d..744e777 100644 --- a/src/message/signalproxy/objects/identity.rs +++ b/src/message/signalproxy/objects/identity.rs @@ -1,119 +1,44 @@ -use crate::primitive::{Variant, VariantMap}; +use libquassel_derive::Network; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Network)] +#[network(repr = "map")] pub struct Identity { + #[network(rename = "identityId")] identity_id: i32, + #[network(rename = "identityName")] identity_name: String, + #[network(rename = "realName")] real_name: String, + #[network(rename = "nicks", override_type = "StringList")] nicks: Vec<String>, + #[network(rename = "awayNick")] away_nick: String, + #[network(rename = "awayNickEnabled")] away_nick_enabled: bool, + #[network(rename = "awayReason")] away_reason: String, + #[network(rename = "awayReasonEnabled")] away_reason_enabled: bool, + #[network(rename = "autoAwayEnabled")] auto_away_enabled: bool, + #[network(rename = "autoAwayTime")] auto_away_time: i32, + #[network(rename = "autoAwayReason")] auto_away_reason: String, + #[network(rename = "autoAwayReasonEnabled")] auto_away_reason_enabled: bool, + #[network(rename = "detachAwayEnabled")] detach_away_enabled: bool, + #[network(rename = "detachAwayReason")] detach_away_reason: String, + #[network(rename = "detachAwayReasonEnabled")] detach_away_reason_enabled: bool, + #[network(rename = "ident")] ident: String, + #[network(rename = "kickReason")] kick_reason: String, + #[network(rename = "partReason")] part_reason: String, + #[network(rename = "quitReason")] quit_reason: String, } - -impl From<VariantMap> for Identity { - fn from(input: VariantMap) -> Self { - Identity { - identity_id: match_variant!(input.get("identityId").unwrap(), Variant::i32), - identity_name: match_variant!(input.get("identityName").unwrap(), Variant::String), - real_name: match_variant!(input.get("realName").unwrap(), Variant::String), - nicks: match_variant!(input.get("nicks").unwrap(), Variant::StringList), - away_nick: match_variant!(input.get("awayNick").unwrap(), Variant::String), - away_nick_enabled: match_variant!(input.get("awayNickEnabled").unwrap(), Variant::bool), - away_reason: match_variant!(input.get("awayReason").unwrap(), Variant::String), - away_reason_enabled: match_variant!( - input.get("awayReasonEnabled").unwrap(), - Variant::bool - ), - auto_away_enabled: match_variant!(input.get("autoAwayEnabled").unwrap(), Variant::bool), - auto_away_time: match_variant!(input.get("autoAwayTime").unwrap(), Variant::i32), - auto_away_reason: match_variant!(input.get("autoAwayReason").unwrap(), Variant::String), - auto_away_reason_enabled: match_variant!( - input.get("autoAwayReasonEnabled").unwrap(), - Variant::bool - ), - detach_away_enabled: match_variant!( - input.get("detachAwayEnabled").unwrap(), - Variant::bool - ), - detach_away_reason: match_variant!( - input.get("detachAwayReason").unwrap(), - Variant::String - ), - detach_away_reason_enabled: match_variant!( - input.get("detachAwayReasonEnabled").unwrap(), - Variant::bool - ), - ident: match_variant!(input.get("ident").unwrap(), Variant::String), - kick_reason: match_variant!(input.get("kickReason").unwrap(), Variant::String), - part_reason: match_variant!(input.get("partReason").unwrap(), Variant::String), - quit_reason: match_variant!(input.get("quitReason").unwrap(), Variant::String), - } - } -} - -impl Into<std::collections::HashMap<String, Variant>> for Identity { - fn into(self) -> VariantMap { - let mut res = VariantMap::with_capacity(19); - - res.insert("identityId".to_string(), Variant::i32(self.identity_id)); - res.insert( - "identityName".to_string(), - Variant::String(self.identity_name), - ); - res.insert("realName".to_string(), Variant::String(self.real_name)); - res.insert("nicks".to_string(), Variant::StringList(self.nicks)); - res.insert("awayNick".to_string(), Variant::String(self.away_nick)); - res.insert( - "awayNickEnabled".to_string(), - Variant::bool(self.away_nick_enabled), - ); - res.insert("awayReason".to_string(), Variant::String(self.away_reason)); - res.insert( - "awayReasonEnabled".to_string(), - Variant::bool(self.away_reason_enabled), - ); - res.insert( - "autoAwayEnabled".to_string(), - Variant::bool(self.auto_away_enabled), - ); - res.insert( - "autoAwayTime".to_string(), - Variant::i32(self.auto_away_time), - ); - res.insert( - "autoAwayReason".to_string(), - Variant::String(self.auto_away_reason), - ); - res.insert( - "detachAwayEnabled".to_string(), - Variant::bool(self.detach_away_enabled), - ); - res.insert( - "detachAwayReason".to_string(), - Variant::String(self.detach_away_reason), - ); - res.insert( - "detachAwayReasonEnabled".to_string(), - Variant::bool(self.detach_away_reason_enabled), - ); - res.insert("ident".to_string(), Variant::String(self.ident)); - res.insert("kickReason".to_string(), Variant::String(self.kick_reason)); - res.insert("partReason".to_string(), Variant::String(self.part_reason)); - res.insert("quitReason".to_string(), Variant::String(self.quit_reason)); - - res - } -} diff --git a/src/message/signalproxy/objects/ircchannel.rs b/src/message/signalproxy/objects/ircchannel.rs index b15d789..ee23f07 100644 --- a/src/message/signalproxy/objects/ircchannel.rs +++ b/src/message/signalproxy/objects/ircchannel.rs @@ -1,22 +1,119 @@ use std::collections::HashMap; +use std::convert::TryFrom; +use std::convert::TryInto; -use crate::primitive::{StringList, Variant, VariantMap}; +use crate::primitive::{StringList, Variant, VariantList, VariantMap}; -#[allow(unused_imports)] use crate::message::signalproxy::Network; #[allow(dead_code)] #[derive(Debug, Clone, PartialEq)] pub struct IrcChannel { - channel_modes_a: HashMap<char, StringList>, - channel_modes_b: HashMap<char, String>, - channel_modes_c: HashMap<char, String>, - channel_modes_d: String, - user_modes: HashMap<String, String>, - name: String, - topic: String, - password: String, - encrypted: bool, + pub channel_modes_a: HashMap<char, StringList>, + pub channel_modes_b: HashMap<char, String>, + pub channel_modes_c: HashMap<char, String>, + pub channel_modes_d: String, + // pub channel_modes: HashMap<char, ChannelMode>, + pub user_modes: HashMap<String, String>, + pub name: String, + pub topic: String, + pub password: String, + pub encrypted: bool, +} + +// #[derive(Debug, Clone, PartialEq)] +// pub enum ChannelMode { +// A(char, StringList), +// B(char, String), +// C(char, String), +// D(char), +// } + +impl Network for Vec<IrcChannel> { + type Item = VariantMap; + + fn to_network(&self) -> Self::Item { + let mut channelmodes: VariantList = Vec::with_capacity(self.len()); + let mut usermodes: VariantList = Vec::with_capacity(self.len()); + let mut name: VariantList = Vec::with_capacity(self.len()); + let mut topic: VariantList = Vec::with_capacity(self.len()); + let mut password: VariantList = Vec::with_capacity(self.len()); + let mut encrypted: VariantList = Vec::with_capacity(self.len()); + + let mut map = VariantMap::new(); + + self.iter().for_each(|item| { + channelmodes.push(Variant::VariantMap({ + let mut map = VariantMap::new(); + + map.insert( + s!("A"), + Variant::VariantMap( + item.channel_modes_a + .iter() + .map(|(k, v)| (k.to_string(), Variant::StringList(v.clone()))) + .collect(), + ), + ); + map.insert( + s!("B"), + Variant::VariantMap( + item.channel_modes_b + .iter() + .map(|(k, v)| (k.to_string(), Variant::String(v.clone()))) + .collect(), + ), + ); + map.insert( + s!("C"), + Variant::VariantMap( + item.channel_modes_c + .iter() + .map(|(k, v)| (k.to_string(), Variant::String(v.clone()))) + .collect(), + ), + ); + map.insert(s!("D"), Variant::String(item.channel_modes_d.clone())); + + map + })); + + usermodes.push(Variant::VariantMap( + item.user_modes + .iter() + .map(|(k, v)| (k.clone(), Variant::String(v.clone()))) + .collect(), + )); + name.push(Variant::String(item.name.clone())); + topic.push(Variant::String(item.topic.clone())); + password.push(Variant::String(item.password.clone())); + encrypted.push(Variant::bool(item.encrypted)); + }); + + map.insert( + String::from("ChanModes"), + Variant::VariantList(channelmodes), + ); + map.insert(String::from("UserModes"), Variant::VariantList(usermodes)); + map.insert(String::from("name"), Variant::VariantList(name)); + map.insert(String::from("topic"), Variant::VariantList(topic)); + map.insert(String::from("password"), Variant::VariantList(password)); + map.insert(String::from("encrypted"), Variant::VariantList(encrypted)); + + map + } + + fn from_network(input: &mut Self::Item) -> Self { + let marker: VariantList = + std::convert::TryInto::try_into(input.get("name").unwrap()).unwrap(); + + let mut res = Vec::new(); + for _ in 0..marker.len() { + res.push(IrcChannel::from_network(input)); + } + + return res; + } } impl Network for IrcChannel { @@ -92,90 +189,48 @@ impl Network for IrcChannel { res } fn from_network(input: &mut Self::Item) -> Self { + let mut chanmodes: VariantMap = match_variant!( + match_variant!(input.get_mut("ChanModes").unwrap(), Variant::VariantList).remove(0), + Variant::VariantMap + ); + Self { - channel_modes_a: match_variant!( - match_variant!( - match_variant!(input.get("ChanModes").unwrap(), Variant::VariantList)[0], - Variant::VariantMap - ) - .get("B") - .unwrap(), - Variant::VariantMap + channel_modes_a: match_variant!(chanmodes.remove("A").unwrap(), Variant::VariantMap) + .into_iter() + .map(|(mut k, v)| (k.remove(0), match_variant!(v, Variant::StringList))) + .collect(), + channel_modes_b: match_variant!(chanmodes.remove("B").unwrap(), Variant::VariantMap) + .into_iter() + .map(|(mut k, v)| (k.remove(0), match_variant!(v, Variant::String))) + .collect(), + channel_modes_c: match_variant!(chanmodes.remove("C").unwrap(), Variant::VariantMap) + .into_iter() + .map(|(mut k, v)| (k.remove(0), match_variant!(v, Variant::String))) + .collect(), + channel_modes_d: match_variant!(chanmodes.remove("D").unwrap(), Variant::String), + user_modes: VariantMap::try_from( + match_variant!(input.get_mut("UserModes").unwrap(), Variant::VariantList).remove(0), ) - .iter() - .map(|(k, v)| { - ( - k.chars().nth(0).unwrap(), - match_variant!(v, Variant::StringList), - ) - }) + .unwrap() + .into_iter() + .map(|(k, v)| (k, v.try_into().unwrap())) .collect(), - channel_modes_b: match_variant!( - match_variant!( - match_variant!(input.get("ChanModes").unwrap(), Variant::VariantList)[0], - Variant::VariantMap - ) - .get("B") + name: match_variant!(input.get_mut("name").unwrap(), Variant::VariantList) + .remove(0) + .try_into() .unwrap(), - Variant::VariantMap - ) - .iter() - .map(|(k, v)| { - ( - k.chars().nth(0).unwrap(), - match_variant!(v, Variant::String), - ) - }) - .collect(), - channel_modes_c: match_variant!( - match_variant!( - match_variant!(input.get("ChanModes").unwrap(), Variant::VariantList)[0], - Variant::VariantMap - ) - .get("C") + topic: match_variant!(input.get_mut("topic").unwrap(), Variant::VariantList) + .remove(0) + .try_into() .unwrap(), - Variant::VariantMap - ) - .iter() - .map(|(k, v)| { - ( - k.chars().nth(0).unwrap(), - match_variant!(v, Variant::String), - ) - }) - .collect(), - channel_modes_d: match_variant!( - match_variant!( - match_variant!(input.get("ChanModes").unwrap(), Variant::VariantList)[0], - Variant::VariantMap - ) - .get("D") + password: match_variant!(input.get_mut("password").unwrap(), Variant::VariantList) + .remove(0) + .try_into() + .unwrap(), + encrypted: match_variant!(input.get_mut("encrypted").unwrap(), Variant::VariantList) + .remove(0) + .try_into() .unwrap(), - Variant::String - ), - user_modes: match_variant!( - match_variant!(input.get("UserModes").unwrap(), Variant::VariantList)[0], - Variant::VariantMap - ) - .iter() - .map(|(k, v)| (k.clone(), match_variant!(v, Variant::String))) - .collect(), - name: match_variant!( - match_variant!(input.get("name").unwrap(), Variant::VariantList)[0], - Variant::String - ), - topic: match_variant!( - match_variant!(input.get("topic").unwrap(), Variant::VariantList)[0], - Variant::String - ), - password: match_variant!( - match_variant!(input.get("password").unwrap(), Variant::VariantList)[0], - Variant::String - ), - encrypted: match_variant!( - match_variant!(input.get("encrypted").unwrap(), Variant::VariantList)[0], - Variant::bool - ), } } } @@ -185,7 +240,7 @@ mod tests { use super::*; fn get_network() -> VariantMap { - VariantMap::from(map! { + map! { s!("encrypted") => Variant::VariantList( vec![ Variant::bool( @@ -233,9 +288,9 @@ mod tests { ), }, ), - s!("A") => Variant::VariantMap(map! - {}, - ), + s!("A") => Variant::VariantMap(map! { + s!("b") => Variant::StringList(vec![s!("*!*@test"), s!("*!*@test2")]), + }), }, ), ], @@ -261,11 +316,11 @@ mod tests { ), ], ) - }) + } } fn get_runtime() -> IrcChannel { IrcChannel { - channel_modes_a: map! {}, + channel_modes_a: map! { 'b' => vec![s!("*!*@test"), s!("*!*@test2")] }, channel_modes_b: map! {}, channel_modes_c: map! { 'j' => s!("5:1"), 'x' => s!("10:5"), 'f' => s!("30:5"), 'F' => s!("5:60") }, channel_modes_d: s!("tCnT"), diff --git a/src/message/signalproxy/objects/ircuser.rs b/src/message/signalproxy/objects/ircuser.rs index 1064965..85fea81 100644 --- a/src/message/signalproxy/objects/ircuser.rs +++ b/src/message/signalproxy/objects/ircuser.rs @@ -1,56 +1,39 @@ -use std::collections::HashMap; - -use crate::primitive::{DateTime, StringList, Variant, VariantMap}; +use crate::primitive::{DateTime, StringList}; #[allow(unused_imports)] use crate::message::signalproxy::Network; use libquassel_derive::Network; -impl Network for Vec<IrcUser> { - type Item = VariantMap; - - fn to_network(&self) -> Self::Item { - Variant::VariantMap(self.iter().fold(HashMap::new(), |mut res, v| { - res.extend(v.to_network()); - - res - })) - } - fn from_network(input: &mut Self::Item) -> Self { - todo!() - } -} - #[allow(dead_code)] #[derive(Debug, Clone, PartialEq, Network)] #[network(repr = "maplist")] pub struct IrcUser { - user: String, - host: String, - nick: String, + pub user: String, + pub host: String, + pub nick: String, #[network(rename = "realName")] - real_name: String, - account: String, - away: bool, + pub real_name: String, + pub account: String, + pub away: bool, #[network(rename = "awayMessage")] - away_message: String, + pub away_message: String, #[network(rename = "idleTime")] - idle_time: DateTime, + pub idle_time: DateTime, #[network(rename = "loginTime")] - login_time: DateTime, - server: String, + pub login_time: DateTime, + pub server: String, #[network(rename = "ircOperator")] - irc_operator: String, + pub irc_operator: String, #[network(rename = "lastAwayMessageTime")] - last_away_message_time: DateTime, + pub last_away_message_time: DateTime, #[network(rename = "whoisServiceReply")] - whois_service_reply: String, + pub whois_service_reply: String, #[network(rename = "suserHost")] - suser_host: String, - encrypted: bool, - channels: StringList, + pub suser_host: String, + pub encrypted: bool, + pub channels: StringList, #[network(rename = "userModes")] - user_modes: String, + pub user_modes: String, } #[cfg(test)] diff --git a/src/message/signalproxy/objects/mod.rs b/src/message/signalproxy/objects/mod.rs index 84f2f5a..2fc4745 100644 --- a/src/message/signalproxy/objects/mod.rs +++ b/src/message/signalproxy/objects/mod.rs @@ -1,15 +1,62 @@ mod aliasmanager; mod buffersyncer; +mod coreinfo; +mod highlightrulemanager; mod identity; -// mod ircchannel; -// mod ircuser; -// mod network; -// mod networkinfo; +mod ircchannel; +mod ircuser; +mod network; +mod networkinfo; + +use std::convert::TryInto; pub use aliasmanager::*; pub use buffersyncer::*; +pub use coreinfo::*; +pub use highlightrulemanager::*; pub use identity::*; +pub use ircchannel::*; +pub use ircuser::*; +pub use network::*; +pub use networkinfo::*; + +use libquassel_derive::From; + +use super::Network; +use crate::primitive::VariantList; + +#[derive(Debug, Clone, PartialEq, From)] +pub enum Types { + AliasManager(AliasManager), + Network(network::Network), + NetworkInfo(NetworkInfo), + NetworkConfig(NetworkConfig), + CoreData(CoreData), + Unknown(VariantList), +} + +impl Types { + pub fn to_network(&self) -> VariantList { + match self { + Types::AliasManager(val) => val.to_network(), + Types::Network(val) => val.to_network(), + Types::NetworkInfo(val) => val.to_network(), + Types::NetworkConfig(val) => val.to_network(), + Types::CoreData(val) => vec![val.to_network().into()], + Types::Unknown(val) => val.clone(), + } + } -pub trait Act { - fn act(self: Self); + pub fn from_network(class_name: &str, input: &mut VariantList) -> Self { + match class_name { + "Network" => Types::Network(Network::from_network(input)), + "NetworkInfo" => Types::NetworkInfo(NetworkInfo::from_network(input)), + "NetworkConfig" => Types::NetworkConfig(NetworkConfig::from_network(input)), + "AliasManager" => Types::AliasManager(AliasManager::from_network(input)), + "CoreData" => Types::CoreData(CoreData::from_network( + &mut input.remove(0).try_into().unwrap(), + )), + _ => Types::Unknown(input.to_owned()), + } + } } diff --git a/src/message/signalproxy/objects/network.rs b/src/message/signalproxy/objects/network.rs index 4075499..6bf9265 100644 --- a/src/message/signalproxy/objects/network.rs +++ b/src/message/signalproxy/objects/network.rs @@ -1,16 +1,17 @@ -use crate::primitive::{StringList, Variant, VariantList, VariantMap}; +use std::convert::TryInto; + +use crate::primitive::{Variant, VariantList, VariantMap}; -#[allow(unused_imports)] use libquassel_derive::Network; use std::collections::HashMap; use num_derive::{FromPrimitive, ToPrimitive}; -use num_traits::{FromPrimitive, ToPrimitive}; +use num_traits::FromPrimitive; use super::{ircchannel::IrcChannel, ircuser::IrcUser, networkinfo::NetworkInfo}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Network { my_nick: String, latency: i32, @@ -105,146 +106,104 @@ impl crate::message::signalproxy::Network for Network { } fn from_network(input: &mut Self::Item) -> Self { - let users_and_channels = match_variant!( - input - .iter() - .nth( - input - .iter() - .position(|x| *x == Variant::ByteArray(s!("IrcUsersAndChannels"))) - .unwrap() - ) - .unwrap(), - Variant::VariantMap - ); - - let res = Self { - my_nick: match_variant!( - input - .iter() - .nth( - input - .iter() - .position(|x| *x == Variant::ByteArray(s!("myNick"))) - .unwrap() - ) - .unwrap(), - Variant::String - ), - latency: match_variant!( - input - .iter() - .nth( - input - .iter() - .position(|x| *x == Variant::ByteArray(s!("latency"))) - .unwrap() - ) - .unwrap(), - Variant::i32 - ), - current_server: match_variant!( - input - .iter() - .nth( - input - .iter() - .position(|x| *x == Variant::ByteArray(s!("currentServer"))) - .unwrap() - ) - .unwrap(), - Variant::String - ), - is_connected: match_variant!( - input - .iter() - .nth( - input - .iter() - .position(|x| *x == Variant::ByteArray(s!("isConnected"))) - .unwrap() - ) - .unwrap(), - Variant::bool - ), - connection_state: ConnectionState::from_i32(match_variant!( - input - .iter() - .nth( - input - .iter() - .position(|x| *x == Variant::ByteArray(s!("connectionState"))) - .unwrap() - ) - .unwrap(), - Variant::i32 - )) - .unwrap(), - irc_users: match_variant!( - users_and_channels.get("Users").unwrap(), - Variant::VariantMap - ) - .iter() - .map(|(k, v)| (k, IrcUser::from_network(v))), - irc_channels: match_variant!( - users_and_channels.get("Channels").unwrap(), - Variant::VariantMap - ) - .iter() - .map(|(k, v)| (k, match_variant!(v, Variant::VariantList))), - supports: match_variant!( - input - .iter() - .nth( - input - .iter() - .position(|x| *x == Variant::ByteArray(s!("Supports"))) - .unwrap() - ) - .unwrap(), - Variant::VariantMap - ) - .iter() - .map(|(k, v)| (k.clone(), match_variant!(v, Variant::String))) - .collect(), - caps: match_variant!( - input - .iter() - .nth( - input - .iter() - .position(|x| *x == Variant::ByteArray(s!("Caps"))) - .unwrap() - ) - .unwrap(), - Variant::VariantMap - ) - .iter() - .map(|(k, v)| (k.clone(), match_variant!(v, Variant::String))) - .collect(), - caps_enabled: match_variant!( - input - .iter() - .nth( - input - .iter() - .position(|x| *x == Variant::ByteArray(s!("CapsEnabled"))) - .unwrap() - ) - .unwrap(), - Variant::VariantList - ) - .iter() - .map(|v| match_variant!(v, Variant::String)) - .collect(), - network_info: NetworkInfo::from_network(input), + let mut i = input.iter().cycle(); + + let users_and_channels: VariantMap = { + i.position(|x| *x == Variant::ByteArray(String::from("IrcUsersAndChannels"))) + .unwrap(); + + i.next().unwrap().try_into().unwrap() }; - todo!() + log::trace!("users and channels: {:#?}", users_and_channels); + + Self { + my_nick: { + i.position(|x| *x == Variant::ByteArray(String::from("myNick"))) + .unwrap(); + + i.next().unwrap().try_into().unwrap() + }, + latency: { + i.position(|x| *x == Variant::ByteArray(String::from("latency"))) + .unwrap(); + + i.next().unwrap().try_into().unwrap() + }, + current_server: { + i.position(|x| *x == Variant::ByteArray(String::from("currentServer"))) + .unwrap(); + + i.next().unwrap().try_into().unwrap() + }, + is_connected: { + i.position(|x| *x == Variant::ByteArray(String::from("isConnected"))) + .unwrap(); + + i.next().unwrap().try_into().unwrap() + }, + connection_state: ConnectionState::from_i32({ + i.position(|x| *x == Variant::ByteArray(String::from("connectionState"))) + .unwrap(); + + i.next().unwrap().try_into().unwrap() + }) + .unwrap(), + irc_users: { + let users: Vec<IrcUser> = Vec::<IrcUser>::from_network( + &mut users_and_channels.get("Users").unwrap().try_into().unwrap(), + ); + users + .into_iter() + .map(|user| (user.nick.clone(), user)) + .collect() + }, + irc_channels: { + let channels: Vec<IrcChannel> = Vec::<IrcChannel>::from_network( + &mut users_and_channels + .get("Channels") + .unwrap() + .try_into() + .unwrap(), + ); + channels + .into_iter() + .map(|channel| (channel.name.clone(), channel)) + .collect() + }, + supports: { + i.position(|x| *x == Variant::ByteArray(String::from("Supports"))) + .unwrap(); + + let var: VariantMap = i.next().unwrap().try_into().unwrap(); + + var.into_iter() + .map(|(k, v)| (k, v.try_into().unwrap())) + .collect() + }, + caps: { + i.position(|x| *x == Variant::ByteArray(String::from("Caps"))) + .unwrap(); + + let var: VariantMap = i.next().unwrap().try_into().unwrap(); + + var.into_iter() + .map(|(k, v)| (k, v.try_into().unwrap())) + .collect() + }, + caps_enabled: { + i.position(|x| *x == Variant::ByteArray(String::from("CapsEnabled"))) + .unwrap(); + + let var: VariantList = i.next().unwrap().try_into().unwrap(); + + var.into_iter().map(|v| v.try_into().unwrap()).collect() + }, + network_info: NetworkInfo::from_network(input), + } } } -#[allow(dead_code)] #[derive(Debug, Clone, PartialEq, Network)] #[network(repr = "map")] pub struct NetworkServer { @@ -274,6 +233,27 @@ pub struct NetworkServer { pub proxy_pass: String, } +#[derive(Debug, Clone, PartialEq, Network)] +#[network(repr = "list")] +pub struct NetworkConfig { + #[network(rename = "pingTimeoutEnabled")] + ping_timeout_enabled: bool, + #[network(rename = "pingInterval")] + ping_interval: i32, + #[network(rename = "maxPingCount")] + max_ping_count: i32, + #[network(rename = "autoWhoEnabled")] + auto_who_enabled: bool, + #[network(rename = "autoWhoInterval")] + auto_who_interval: i32, + #[network(rename = "autoWhoNickLimit")] + auto_who_nick_limit: i32, + #[network(rename = "autoWhoDelay")] + auto_who_delay: i32, + #[network(rename = "standardCtcp")] + standard_ctcp: bool, +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/message/signalproxy/objects/networkinfo.rs b/src/message/signalproxy/objects/networkinfo.rs index e36297d..e5aa6ea 100644 --- a/src/message/signalproxy/objects/networkinfo.rs +++ b/src/message/signalproxy/objects/networkinfo.rs @@ -1,23 +1,16 @@ -use crate::primitive::{StringList, Variant}; +use crate::primitive::StringList; -#[allow(unused_imports)] use libquassel_derive::Network; use crate::message::objects::network::NetworkServer; -#[allow(dead_code)] #[derive(Debug, Clone, PartialEq, Network)] #[network(repr = "list")] pub struct NetworkInfo { #[network(rename = "networkName")] pub network_name: String, - #[network( - rename = "ServerList", - override_type = "VariantList", - to_map = "|server| Variant::VariantMap(server.to_network())", - from_map = "|server| NetworkServer::from_network(&mut match_variant!(server, Variant::VariantMap))" - )] + #[network(rename = "ServerList", network, variant = "VariantList")] pub server_list: Vec<NetworkServer>, #[network(rename = "perform")] pub perform: StringList, @@ -75,15 +68,12 @@ pub struct NetworkInfo { #[cfg(test)] mod tests { - use crate::{ - message::objects::network::NetworkServer, - primitive::{Variant, VariantList}, - }; + use crate::primitive::{Variant, VariantList}; use super::*; use crate::message::signalproxy::translation::Network; - use pretty_assertions::{assert_eq, assert_ne}; + use pretty_assertions::assert_eq; fn get_network() -> VariantList { vec![ diff --git a/src/message/signalproxy/rpccall.rs b/src/message/signalproxy/rpccall.rs index 2755ea1..0b5c598 100644 --- a/src/message/signalproxy/rpccall.rs +++ b/src/message/signalproxy/rpccall.rs @@ -1,7 +1,7 @@ use crate::message::MessageType; use crate::primitive::Message; use crate::primitive::{Variant, VariantList}; -use crate::{Deserialize, Serialize}; +use crate::{deserialize::Deserialize, serialize::Serialize}; #[derive(Clone, Debug, std::cmp::PartialEq)] pub enum RpcCall { diff --git a/src/message/signalproxy/syncmessage.rs b/src/message/signalproxy/syncmessage.rs index b58659a..9c1716b 100644 --- a/src/message/signalproxy/syncmessage.rs +++ b/src/message/signalproxy/syncmessage.rs @@ -1,6 +1,6 @@ use crate::message::MessageType; use crate::primitive::{Variant, VariantList}; -use crate::{Deserialize, Serialize}; +use crate::{deserialize::Deserialize, serialize::Serialize}; #[derive(Clone, Debug, std::cmp::PartialEq)] pub struct SyncMessage { diff --git a/src/primitive/bufferinfo.rs b/src/primitive/bufferinfo.rs index 24080cb..97d9408 100644 --- a/src/primitive/bufferinfo.rs +++ b/src/primitive/bufferinfo.rs @@ -2,8 +2,7 @@ use std::vec::Vec; use failure::Error; -use crate::{Deserialize, DeserializeUTF8}; -use crate::{Serialize, SerializeUTF8}; +use crate::{deserialize::*, serialize::*}; /// The BufferInfo struct represents a BufferInfo as received in IRC /// diff --git a/src/primitive/datetime.rs b/src/primitive/datetime.rs index e4c4c83..8d78b32 100644 --- a/src/primitive/datetime.rs +++ b/src/primitive/datetime.rs @@ -1,5 +1,4 @@ -use crate::Deserialize; -use crate::Serialize; +use crate::{deserialize::*, serialize::*}; use time::{OffsetDateTime, PrimitiveDateTime, UtcOffset}; diff --git a/src/primitive/message.rs b/src/primitive/message.rs index 04c427e..36a55c5 100644 --- a/src/primitive/message.rs +++ b/src/primitive/message.rs @@ -1,9 +1,10 @@ use std::vec::Vec; +use num_derive::{FromPrimitive, ToPrimitive}; + use failure::Error; -use crate::{Deserialize, DeserializeUTF8}; -use crate::{Serialize, SerializeUTF8}; +use crate::{deserialize::*, serialize::*}; use crate::primitive::BufferInfo; @@ -171,8 +172,9 @@ impl Deserialize for Message { } #[repr(i32)] -#[derive(Copy, Clone, Debug, std::cmp::PartialEq)] +#[derive(Copy, Clone, Debug, std::cmp::PartialEq, FromPrimitive, ToPrimitive)] pub enum MessageType { + None = 0x00000000, Plain = 0x00000001, Notice = 0x00000002, Action = 0x00000004, diff --git a/src/primitive/signedint.rs b/src/primitive/signedint.rs index a1254fb..2d2029d 100644 --- a/src/primitive/signedint.rs +++ b/src/primitive/signedint.rs @@ -6,7 +6,7 @@ use std::vec::Vec; use failure::Error; -use crate::{Deserialize, Serialize}; +use crate::{deserialize::*, serialize::*}; impl Serialize for i64 { fn serialize(&self) -> Result<Vec<u8>, Error> { diff --git a/src/primitive/string.rs b/src/primitive/string.rs index 7ea838d..478bc0a 100644 --- a/src/primitive/string.rs +++ b/src/primitive/string.rs @@ -8,9 +8,7 @@ use failure::Error; use log::trace; use crate::util; -use crate::{Deserialize, DeserializeUTF8, Serialize, SerializeUTF8}; - -pub type ByteArray = String; +use crate::{deserialize::*, serialize::*}; /// We Shadow the String type here as we can only use impl on types in our own scope. /// @@ -21,10 +19,8 @@ impl Serialize for String { fn serialize(&self) -> Result<Vec<u8>, Error> { let mut res: Vec<u8> = Vec::new(); - let utf16: Vec<u16> = self.encode_utf16().collect(); - for i in utf16 { - res.extend(i.to_be_bytes().iter()); - } + self.encode_utf16() + .for_each(|i| res.extend(i.to_be_bytes().iter())); util::prepend_byte_len(&mut res); return Ok(res); diff --git a/src/primitive/stringlist.rs b/src/primitive/stringlist.rs index df6d281..ceea63e 100644 --- a/src/primitive/stringlist.rs +++ b/src/primitive/stringlist.rs @@ -8,7 +8,7 @@ use failure::Error; use log::trace; -use crate::{Deserialize, Serialize}; +use crate::{deserialize::*, serialize::*}; /// StringList are represented as a Vec of Strings /// diff --git a/src/primitive/unsignedint.rs b/src/primitive/unsignedint.rs index 6e91e2a..90ec696 100644 --- a/src/primitive/unsignedint.rs +++ b/src/primitive/unsignedint.rs @@ -8,7 +8,7 @@ use std::vec::Vec; use failure::Error; use crate::error::ProtocolError; -use crate::{Deserialize, Serialize}; +use crate::{deserialize::*, serialize::*}; impl Serialize for bool { fn serialize(&self) -> Result<Vec<u8>, Error> { diff --git a/src/primitive/variant.rs b/src/primitive/variant.rs index 8a98d59..efe69b8 100644 --- a/src/primitive/variant.rs +++ b/src/primitive/variant.rs @@ -7,8 +7,7 @@ use log::{error, trace}; use crate::error::ProtocolError; use crate::primitive; use crate::primitive::StringList; -use crate::{Deserialize, DeserializeUTF8}; -use crate::{Serialize, SerializeUTF8}; +use crate::{deserialize::*, serialize::*}; use crate::primitive::{BufferInfo, Date, DateTime, Message, Time, VariantList, VariantMap}; @@ -35,6 +34,7 @@ pub enum Variant { DateTime(DateTime), VariantMap(VariantMap), VariantList(VariantList), + #[from(ignore)] String(String), #[from(ignore)] ByteArray(String), @@ -50,6 +50,32 @@ pub enum Variant { i8(i8), } +impl From<Variant> for String { + fn from(input: Variant) -> Self { + match input { + Variant::String(value) => value, + Variant::ByteArray(value) => value, + _ => panic!("unknown variant expected string or bytearray"), + } + } +} + +impl From<&Variant> for String { + fn from(input: &Variant) -> Self { + match input { + Variant::String(value) => value.clone(), + Variant::ByteArray(value) => value.clone(), + _ => panic!("unknown variant expected string or bytearray"), + } + } +} + +impl From<String> for Variant { + fn from(input: String) -> Self { + Self::String(input) + } +} + impl Serialize for Variant { fn serialize(&self) -> Result<Vec<u8>, Error> { let unknown: u8 = 0x00; diff --git a/src/primitive/variantlist.rs b/src/primitive/variantlist.rs index 7e74122..2d20ae2 100644 --- a/src/primitive/variantlist.rs +++ b/src/primitive/variantlist.rs @@ -5,7 +5,7 @@ use failure::Error; use log::trace; -use crate::{Deserialize, Serialize}; +use crate::{deserialize::*, serialize::*}; use crate::primitive::Variant; diff --git a/src/primitive/variantmap.rs b/src/primitive/variantmap.rs index d43028c..8c1032d 100644 --- a/src/primitive/variantmap.rs +++ b/src/primitive/variantmap.rs @@ -5,8 +5,7 @@ use failure::Error; use log::trace; -use crate::Deserialize; -use crate::Serialize; +use crate::{deserialize::*, serialize::*}; use crate::primitive::Variant; use crate::util; @@ -40,11 +39,11 @@ impl Deserialize for VariantMap { let mut pos: usize = 4; let mut map = VariantMap::new(); for _ in 0..len { - trace!(target: "primitive::VariantMap", "Parsing entry name"); + trace!(target: "primitive::VariantMap", "Parsing entry name {:x?}", &b[pos..]); let (nlen, name) = String::parse(&b[pos..])?; pos += nlen; - trace!(target: "primitive::VariantMap", "Parsing entry: {:?} with len {:?}", name, &b[(pos)..(pos + 4)]); + trace!(target: "primitive::VariantMap", "Parsing entry: {:?} with type {:x?}", name, &b[(pos)..(pos + 4)]); let (vlen, value) = Variant::parse(&b[(pos)..])?; pos += vlen; |
