From e4338a9d9d0a76029bca376c2bcb499962575e87 Mon Sep 17 00:00:00 2001 From: Max Audron Date: Thu, 27 Feb 2025 01:03:29 +0100 Subject: extend docs and reorganize files --- README.md | 36 +- derive/src/lib.rs | 8 +- src/error.rs | 45 + src/error/mod.rs | 45 - src/lib.rs | 93 +- src/message/signalproxy/mod.rs | 2 +- src/message/signalproxy/translation.rs | 122 +++ src/message/signalproxy/translation/aos_soa.rs | 1134 ------------------------ src/message/signalproxy/translation/mod.rs | 112 --- src/primitive/variant.rs | 2 +- src/serialize.rs | 66 +- src/session.rs | 252 ++++++ src/session/mod.rs | 242 ----- src/util.rs | 13 + 14 files changed, 606 insertions(+), 1566 deletions(-) create mode 100644 src/error.rs delete mode 100644 src/error/mod.rs create mode 100644 src/message/signalproxy/translation.rs delete mode 100644 src/message/signalproxy/translation/aos_soa.rs delete mode 100644 src/message/signalproxy/translation/mod.rs create mode 100644 src/session.rs delete mode 100644 src/session/mod.rs diff --git a/README.md b/README.md index 60de75d..b14387d 100644 --- a/README.md +++ b/README.md @@ -17,20 +17,22 @@ Native rust implementation of the Quassel protocol and library functions # TODOs -- [TODO] Implementation of Syncable Objects {#implementation-of-syncable-objects} - - [DONE] AliasManager - - [TODO] BacklogManager - - [DONE] BufferSyncer - - [DONE] BufferViewConfig - - [DONE] BufferViewManager - - [DONE] CertManager - - [DONE] CoreInfo - - [DONE] HighlightRuleManager - - [DONE] Identity - - [DONE] IgnoreListManager - - [DONE] IrcChannel - - [TODO] IrcListHelper - - [TODO] IrcUser - - [TODO] Network - - [TODO] NetworkInfo - - [TODO] NetworkConfig +- [ ] Implementation of Syncable Objects + - [X] AliasManager + - [ ] BacklogManager + - [X] BufferSyncer + - [X] BufferViewConfig + - [X] BufferViewManager + - [X] CertManager + - [X] CoreInfo + - [X] HighlightRuleManager + - [X] Identity + - [X] IgnoreListManager + - [X] IrcChannel + - [ ] IrcListHelper + - [X] IrcUser + - [X] Network + - [X] NetworkInfo + - [X] NetworkConfig +- [ ] Rework Error handling to actually handle errors +- [ ] Rewrite the parsers using nom diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 71feae7..47e8ad8 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -2,8 +2,8 @@ use syn; mod from; mod network; -mod sync; mod setters; +mod sync; #[proc_macro_derive(NetworkList, attributes(network))] pub fn network_list(input: proc_macro::TokenStream) -> proc_macro::TokenStream { @@ -25,12 +25,6 @@ pub fn setters(input: proc_macro::TokenStream) -> proc_macro::TokenStream { setters::setters(input) } -/// Sugar to make sending sync messages nicer -/// -/// Example: -/// ``` -/// sync!("requestCreateBufferView", [properties.to_network_map()]) -/// ``` #[proc_macro] pub fn sync(input: proc_macro::TokenStream) -> proc_macro::TokenStream { sync::sync(input) diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..7414062 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,45 @@ +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum ProtocolError { + #[error("message has wrong type")] + WrongMsgType, + #[error("bool value is neither 0 nor 1")] + BoolOutOfRange, + #[error("QVariant is not known")] + UnknownVariant, + #[error("UserType is not known: {0}")] + UnknownUserType(String), + #[error("wrong variant has been given")] + WrongVariant, + #[error("io error: {0}")] + IOError(#[from] std::io::Error), + #[error("could not convert from int: {0}")] + TryFromIntError(#[from] std::num::TryFromIntError), + #[error("utf8 error: {0}")] + Utf8Error(#[from] std::string::FromUtf8Error), + #[error("errored to parse char as utf16")] + CharError, + #[error("failed to deal with time: {0}")] + TimeError(#[from] time::error::ComponentRange), +} + +// impl std::error::Error for ErrorKind {} +// +// impl std::convert::From for ErrorKind { +// fn from(error: std::io::Error) -> Self { +// ErrorKind::IOError(error) +// } +// } +// +// impl std::convert::From for ErrorKind { +// fn from(error: std::num::TryFromIntError) -> Self { +// ErrorKind::TryFromIntError(error) +// } +// } +// +// impl std::convert::From for ErrorKind { +// fn from(error: std::string::FromUtf8Error) -> Self { +// ErrorKind::Utf8Error(error) +// } +// } diff --git a/src/error/mod.rs b/src/error/mod.rs deleted file mode 100644 index 7414062..0000000 --- a/src/error/mod.rs +++ /dev/null @@ -1,45 +0,0 @@ -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum ProtocolError { - #[error("message has wrong type")] - WrongMsgType, - #[error("bool value is neither 0 nor 1")] - BoolOutOfRange, - #[error("QVariant is not known")] - UnknownVariant, - #[error("UserType is not known: {0}")] - UnknownUserType(String), - #[error("wrong variant has been given")] - WrongVariant, - #[error("io error: {0}")] - IOError(#[from] std::io::Error), - #[error("could not convert from int: {0}")] - TryFromIntError(#[from] std::num::TryFromIntError), - #[error("utf8 error: {0}")] - Utf8Error(#[from] std::string::FromUtf8Error), - #[error("errored to parse char as utf16")] - CharError, - #[error("failed to deal with time: {0}")] - TimeError(#[from] time::error::ComponentRange), -} - -// impl std::error::Error for ErrorKind {} -// -// impl std::convert::From for ErrorKind { -// fn from(error: std::io::Error) -> Self { -// ErrorKind::IOError(error) -// } -// } -// -// impl std::convert::From for ErrorKind { -// fn from(error: std::num::TryFromIntError) -> Self { -// ErrorKind::TryFromIntError(error) -// } -// } -// -// impl std::convert::From for ErrorKind { -// fn from(error: std::string::FromUtf8Error) -> Self { -// ErrorKind::Utf8Error(error) -// } -// } diff --git a/src/lib.rs b/src/lib.rs index 59581ae..d3d7d03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,9 @@ +#![doc = include_str!("../README.md")] #![cfg_attr(all(test, feature = "bench"), feature(test))] + #[cfg(all(test, feature = "bench"))] extern crate test; -#[doc = include_str!("../README.md")] #[cfg_attr(docsrs, feature(doc_cfg))] extern crate self as libquassel; @@ -50,3 +51,93 @@ pub trait HandshakeDeserialize { where Self: std::marker::Sized; } + +/// Derive the [message::NetworkList] and [message::NetworkMap] traits. +/// +/// This provides easy and boilerplate free implementations for the network translations from and to the raw +/// Variants. For details on the different kinds of mappings see [message::translation] +/// +/// ```rust ignore +/// use libquassel::{NetworkList, NetworkMap}; +/// +/// #[derive(NetworkMap, NetworkList)] +/// #[network(repr = "map")] +/// pub struct Example { +/// #[network(rename = "Name", default)] +/// pub name: String, +/// #[network(rename = "Nested", network = "map", variant = "VariantMap")] +/// pub nested: Nested, +/// } +/// +/// type Value = i32; +/// +/// #[derive(NetworkMap, NetworkList)] +/// #[network(repr = "maplist")] +/// pub struct Nested { +/// #[network(rename = "Name", type = "i32")] +/// pub field: Value +/// } +/// ``` +/// +/// ## Attributes +/// ### Object Attributes +/// - `#[network(repr = "...")]` +/// +/// Sets the network representation used for this object. Only applies to [NetworkMap] +/// +/// Either [`maplist`] or [`map`] +/// +/// ### Field Attributes +/// - `#[network(rename = "name")]` +/// +/// Renames the field to change case or spelling etc. +/// +/// - `#[network(skip)]` +/// +/// Ignore this field completly in both to and from conversions. +/// - `#[network(default)]` +/// +/// If the field is not set in the network representation, fall back on [`Default::default()`]. +/// +/// May be used if the network representation does not always contain a field. +/// +/// - `#[network(network = "...")]` +/// +/// Use either NetworkMap or NetworkList translation trait to convert this field depending +/// on if `map` or `list` is set respectivly. +/// +/// - `#[network(variant = "...")]` +/// +/// Override the variant used to convert the field, instead of the fields type itself. +/// +/// E.g if the field is of type `String` but the network representation wants `ByteArray` then +/// set this here. +/// +/// - `#[network(type = "...")]` +/// +/// Override the type of the field that is used e.g. when you are using a type alias. +/// +/// This differs from setting the `variant` as it will also influence the inner type instead +/// of just the wrapping Variant +/// +/// - `#[network(stringlist)]` +/// +/// Special case handling for if the field type is `Vec`. +/// +/// This would by default be converted to a `Variant::VariantList(Vec)` but for +/// strings the [StringList] representation is used: `Variant::StringList(Vec)` +/// +/// [`maplist`]: libquassel::message::translation#structure-of-arrays +/// [`map`]: libquassel::message::translation#variantmap +/// [StringList]: libquassel::primitive::StringList +pub use libquassel_derive::{NetworkList, NetworkMap}; + +/// send a syncmessage using [`Syncable::send_sync`] and casts it's arguments to the correct Variants +/// +/// Example: +/// ```rust ignore +/// sync!("requestCreateBufferView", [properties.to_network_map()]) +/// ``` +/// +/// [`Syncable::send_sync`]: libquassel::message::Syncable::send_sync +pub use libquassel_derive::sync; diff --git a/src/message/signalproxy/mod.rs b/src/message/signalproxy/mod.rs index 6cd887b..062b400 100644 --- a/src/message/signalproxy/mod.rs +++ b/src/message/signalproxy/mod.rs @@ -15,7 +15,7 @@ pub mod objects; mod rpccall; mod syncmessage; -mod translation; +pub mod translation; pub use translation::*; pub use heartbeat::*; diff --git a/src/message/signalproxy/translation.rs b/src/message/signalproxy/translation.rs new file mode 100644 index 0000000..b175e44 --- /dev/null +++ b/src/message/signalproxy/translation.rs @@ -0,0 +1,122 @@ +//! Traits for Network Representation translation +//! +//! The traits found here usually do not need to implemented manually and can be derived using the [`libquassel::NetworkList`] and [`libquassel::NetworkMap`] macros. +//! +//! Quassel has 3 main ways to represent an object over the Network: +//! +//! ### VariantList +//! The struct is serialized to a Vector of Variants. This is mostly used in the InitData messages. +//! First the field name as a ByteArray (UTF-8 String), followed by the field value in it's own Type's va!iant. +//! The order in which the fields are transmitted cannot be assumed. +//! +//! Example: +//! ```ignore +//! NetworkConfig { +//! ping_timeout_enabled: true, +//! ping_interval: 0, +//! } +//! ``` +//! to +//! ```ignore +//! VariantList([ +//! ByteArray( +//! "pingTimeoutEnabled", +//! ), +//! bool( +//! true, +//! ), +//! ByteArray( +//! "pingInterval", +//! ), +//! i32( +//! 30, +//! ), +//! ]) +//! ``` +//! +//! +//! ### VariantMap +//! The struct is represented as a `VariantMap`. The keys and values of +//! the struct are serialized to a corresponding `HashMap`. +//! +//! Using the [libquassel::NetworkMap] macro this is selected with `#[network(repr = "map")]` on the object. +//! +//! Example: +//! ```ignore +//! NetworkConfig { +//! ping_timeout_enabled: false, +//! ping_interval: 0, +//! } +//! ``` +//! to +//! ```ignore +//! VariantMap({ +//! "pingTimeoutEnabled": bool(false) +//! "pingInterval": i32(0) +//! }) +//! ``` +//! +//! ### Structure of Arrays +//! +//! For Objects that are transmitted as multiple at once the VariantMap +//! representation is augemented and instead of transmitting multiple `VariantMaps`, +//! each field is a `VariantList` of Items. +//! +//! Using the [libquassel::NetworkMap] macro this is selected with `#[network(repr = "maplist")]` on the +//! object. +//! +//! Example: +//! ```ignore +//! vec![ +//! NetworkConfig { +//! ping_timeout_enabled: false, +//! ping_interval: 0, +//! }, +//! NetworkConfig { +//! ping_timeout_enabled: true, +//! ping_interval: 1, +//! }, +//! ] +//! ``` +//! to +//! ```ignore +//! VariantMap({ +//! "pingTimeoutEnabled": VariantList([ +//! bool(false), +//! bool(true), +//! ]), +//! "pingInterval": VariantList([ +//! i32(0), +//! i32(1), +//! ]) +//! }) +//! ``` +use crate::primitive::{Variant, VariantList}; + +#[deprecated( + since = "0.1.0", + note = "please use NetworkMap and NetworkList implementations" +)] +pub trait Network { + type Item; + + fn to_network(&self) -> Self::Item; + fn from_network(input: &mut Self::Item) -> Self; +} + +pub trait NetworkMap +where + // TODO correct this error type + Self::Item: TryFrom, + Self::Item: Into, +{ + type Item; + + fn to_network_map(&self) -> Self::Item; + fn from_network_map(input: &mut Self::Item) -> Self; +} + +pub trait NetworkList { + fn to_network_list(&self) -> VariantList; + fn from_network_list(input: &mut VariantList) -> Self; +} diff --git a/src/message/signalproxy/translation/aos_soa.rs b/src/message/signalproxy/translation/aos_soa.rs deleted file mode 100644 index c070aea..0000000 --- a/src/message/signalproxy/translation/aos_soa.rs +++ /dev/null @@ -1,1134 +0,0 @@ -// InitData { -// class_name: "AliasManager", -// object_name: "", -// init_data: [ -// Variant::ByteArray( -// "Aliases", -// ), -// Variant::VariantMap( -// { -// "names": Variant::StringList( -// [ -// "j", -// "ns", -// "nickserv", -// "cs", -// "chanserv", -// "hs", -// "hostserv", -// "wii", -// "back", -// "inxi", -// "sysinfo", -// ], -// ), -// "expansions": Variant::StringList( -// [ -// "/join $0", -// "/msg nickserv $0", -// "/msg nickserv $0", -// "/msg chanserv $0", -// "/msg chanserv $0", -// "/msg hostserv $0", -// "/msg hostserv $0", -// "/whois $0 $0", -// "/quote away", -// "/exec inxi $0", -// "/exec inxi -d", -// ], -// ), -// }, -// ), -// ], -// }, - -// InitData { -// class_name: "Network", -// object_name: "2", -// init_data: [ -// ByteArray( -// "Caps", -// ), -// VariantMap( -// { -// "message-tags": String( -// "", -// ), -// "multi-prefix": String( -// "", -// ), -// "echo-message": String( -// "", -// ), -// "away-notify": String( -// "", -// ), -// "inspircd.org/standard-replies": String( -// "", -// ), -// "sasl": String( -// "", -// ), -// "labeled-response": String( -// "", -// ), -// "inspircd.org/poison": String( -// "", -// ), -// "account-tag": String( -// "", -// ), -// "userhost-in-names": String( -// "", -// ), -// "account-notify": String( -// "", -// ), -// "extended-join": String( -// "", -// ), -// "server-time": String( -// "", -// ), -// "chghost": String( -// "", -// ), -// "setname": String( -// "", -// ), -// "cap-notify": String( -// "", -// ), -// "batch": String( -// "", -// ), -// "sts": String( -// "duration=15552000,preload", -// ), -// "invite-notify": String( -// "", -// ), -// }, -// ), -// ByteArray( -// "CapsEnabled", -// ), -// VariantList( -// [ -// String( -// "account-notify", -// ), -// String( -// "away-notify", -// ), -// String( -// "cap-notify", -// ), -// String( -// "chghost", -// ), -// String( -// "extended-join", -// ), -// String( -// "multi-prefix", -// ), -// String( -// "userhost-in-names", -// ), -// ], -// ), -// ByteArray( -// "IrcUsersAndChannels", -// ), -// VariantMap( -// { -// "Users": VariantMap( -// { -// "suserHost": VariantList( -// [ -// String( -// "", -// ), -// String( -// "", -// ), -// String( -// "", -// ), -// String( -// "", -// ), -// String( -// "", -// ), -// ], -// ), -// "lastAwayMessageTime": VariantList( -// [ -// DateTime( -// OffsetDateTime { -// utc_datetime: PrimitiveDateTime { -// date: Date { -// year: 1970, -// ordinal: 1, -// }, -// time: Time { -// hour: 0, -// minute: 0, -// second: 0, -// nanosecond: 0, -// }, -// }, -// offset: UtcOffset { -// seconds: 0, -// }, -// }, -// ), -// DateTime( -// OffsetDateTime { -// utc_datetime: PrimitiveDateTime { -// date: Date { -// year: 1970, -// ordinal: 1, -// }, -// time: Time { -// hour: 0, -// minute: 0, -// second: 0, -// nanosecond: 0, -// }, -// }, -// offset: UtcOffset { -// seconds: 0, -// }, -// }, -// ), -// DateTime( -// OffsetDateTime { -// utc_datetime: PrimitiveDateTime { -// date: Date { -// year: 1970, -// ordinal: 1, -// }, -// time: Time { -// hour: 0, -// minute: 0, -// second: 0, -// nanosecond: 0, -// }, -// }, -// offset: UtcOffset { -// seconds: 0, -// }, -// }, -// ), -// DateTime( -// OffsetDateTime { -// utc_datetime: PrimitiveDateTime { -// date: Date { -// year: 1970, -// ordinal: 1, -// }, -// time: Time { -// hour: 0, -// minute: 0, -// second: 0, -// nanosecond: 0, -// }, -// }, -// offset: UtcOffset { -// seconds: 0, -// }, -// }, -// ), -// DateTime( -// OffsetDateTime { -// utc_datetime: PrimitiveDateTime { -// date: Date { -// year: 1970, -// ordinal: 1, -// }, -// time: Time { -// hour: 0, -// minute: 0, -// second: 0, -// nanosecond: 0, -// }, -// }, -// offset: UtcOffset { -// seconds: 0, -// }, -// }, -// ), -// ], -// ), -// "away": VariantList( -// [ -// bool( -// false, -// ), -// bool( -// false, -// ), -// bool( -// false, -// ), -// bool( -// false, -// ), -// bool( -// false, -// ), -// ], -// ), -// "ircOperator": VariantList( -// [ -// String( -// "", -// ), -// String( -// "", -// ), -// String( -// "", -// ), -// String( -// "", -// ), -// String( -// "", -// ), -// ], -// ), -// "account": VariantList( -// [ -// String( -// "", -// ), -// String( -// "*", -// ), -// String( -// "", -// ), -// String( -// "", -// ), -// String( -// "audron", -// ), -// ], -// ), -// "loginTime": VariantList( -// [ -// DateTime( -// OffsetDateTime { -// utc_datetime: PrimitiveDateTime { -// date: Date { -// year: 1970, -// ordinal: 1, -// }, -// time: Time { -// hour: 0, -// minute: 0, -// second: 0, -// nanosecond: 0, -// }, -// }, -// offset: UtcOffset { -// seconds: 0, -// }, -// }, -// ), -// DateTime( -// OffsetDateTime { -// utc_datetime: PrimitiveDateTime { -// date: Date { -// year: 1970, -// ordinal: 1, -// }, -// time: Time { -// hour: 0, -// minute: 0, -// second: 0, -// nanosecond: 0, -// }, -// }, -// offset: UtcOffset { -// seconds: 0, -// }, -// }, -// ), -// DateTime( -// OffsetDateTime { -// utc_datetime: PrimitiveDateTime { -// date: Date { -// year: 1970, -// ordinal: 1, -// }, -// time: Time { -// hour: 0, -// minute: 0, -// second: 0, -// nanosecond: 0, -// }, -// }, -// offset: UtcOffset { -// seconds: 0, -// }, -// }, -// ), -// DateTime( -// OffsetDateTime { -// utc_datetime: PrimitiveDateTime { -// date: Date { -// year: 1970, -// ordinal: 1, -// }, -// time: Time { -// hour: 0, -// minute: 0, -// second: 0, -// nanosecond: 0, -// }, -// }, -// offset: UtcOffset { -// seconds: 0, -// }, -// }, -// ), -// DateTime( -// OffsetDateTime { -// utc_datetime: PrimitiveDateTime { -// date: Date { -// year: 1970, -// ordinal: 1, -// }, -// time: Time { -// hour: 0, -// minute: 0, -// second: 0, -// nanosecond: 0, -// }, -// }, -// offset: UtcOffset { -// seconds: 0, -// }, -// }, -// ), -// ], -// ), -// "userModes": VariantList( -// [ -// String( -// "", -// ), -// String( -// "Iwxi", -// ), -// String( -// "", -// ), -// String( -// "", -// ), -// String( -// "", -// ), -// ], -// ), -// "host": VariantList( -// [ -// String( -// "services", -// ), -// String( -// "irc-tar.hr0.2d7ge7.IP", -// ), -// String( -// "services", -// ), -// String( -// "", -// ), -// String( -// "user/audron", -// ), -// ], -// ), -// "whoisServiceReply": VariantList( -// [ -// String( -// "", -// ), -// String( -// "", -// ), -// String( -// "", -// ), -// String( -// "", -// ), -// String( -// "", -// ), -// ], -// ), -// "channels": VariantList( -// [ -// StringList( -// [], -// ), -// StringList( -// [ -// "#audron-test", -// ], -// ), -// StringList( -// [], -// ), -// StringList( -// [], -// ), -// StringList( -// [ -// "#audron-test", -// ], -// ), -// ], -// ), -// "realName": VariantList( -// [ -// String( -// "", -// ), -// String( -// "Quassel IRC User", -// ), -// String( -// "", -// ), -// String( -// "", -// ), -// String( -// "Max Audron ", -// ), -// ], -// ), -// "nick": VariantList( -// [ -// String( -// "NickServ", -// ), -// String( -// "audron_", -// ), -// String( -// "Global", -// ), -// String( -// "irc.snoonet.org", -// ), -// String( -// "audron", -// ), -// ], -// ), -// "idleTime": VariantList( -// [ -// DateTime( -// OffsetDateTime { -// utc_datetime: PrimitiveDateTime { -// date: Date { -// year: 1970, -// ordinal: 1, -// }, -// time: Time { -// hour: 0, -// minute: 0, -// second: 0, -// nanosecond: 0, -// }, -// }, -// offset: UtcOffset { -// seconds: 0, -// }, -// }, -// ), -// DateTime( -// OffsetDateTime { -// utc_datetime: PrimitiveDateTime { -// date: Date { -// year: 1970, -// ordinal: 1, -// }, -// time: Time { -// hour: 0, -// minute: 0, -// second: 0, -// nanosecond: 0, -// }, -// }, -// offset: UtcOffset { -// seconds: 0, -// }, -// }, -// ), -// DateTime( -// OffsetDateTime { -// utc_datetime: PrimitiveDateTime { -// date: Date { -// year: 1970, -// ordinal: 1, -// }, -// time: Time { -// hour: 0, -// minute: 0, -// second: 0, -// nanosecond: 0, -// }, -// }, -// offset: UtcOffset { -// seconds: 0, -// }, -// }, -// ), -// DateTime( -// OffsetDateTime { -// utc_datetime: PrimitiveDateTime { -// date: Date { -// year: 1970, -// ordinal: 1, -// }, -// time: Time { -// hour: 0, -// minute: 0, -// second: 0, -// nanosecond: 0, -// }, -// }, -// offset: UtcOffset { -// seconds: 0, -// }, -// }, -// ), -// DateTime( -// OffsetDateTime { -// utc_datetime: PrimitiveDateTime { -// date: Date { -// year: 1970, -// ordinal: 1, -// }, -// time: Time { -// hour: 0, -// minute: 0, -// second: 0, -// nanosecond: 0, -// }, -// }, -// offset: UtcOffset { -// seconds: 0, -// }, -// }, -// ), -// ], -// ), -// "encrypted": VariantList( -// [ -// bool( -// false, -// ), -// bool( -// false, -// ), -// bool( -// false, -// ), -// bool( -// false, -// ), -// bool( -// false, -// ), -// ], -// ), -// "awayMessage": VariantList( -// [ -// String( -// "", -// ), -// String( -// "", -// ), -// String( -// "", -// ), -// String( -// "", -// ), -// String( -// "", -// ), -// ], -// ), -// "user": VariantList( -// [ -// String( -// "NickServ", -// ), -// String( -// "quassel", -// ), -// String( -// "Global", -// ), -// String( -// "", -// ), -// String( -// "audron", -// ), -// ], -// ), -// "server": VariantList( -// [ -// String( -// "", -// ), -// String( -// "irc.snoonet.org", -// ), -// String( -// "", -// ), -// String( -// "", -// ), -// String( -// "irc.snoonet.org", -// ), -// ], -// ), -// }, -// ), -// "Channels": VariantMap( -// { -// "encrypted": VariantList( -// [ -// bool( -// false, -// ), -// ], -// ), -// "topic": VariantList( -// [ -// String( -// "", -// ), -// ], -// ), -// "password": VariantList( -// [ -// String( -// "", -// ), -// ], -// ), -// "ChanModes": VariantList( -// [ -// VariantMap( -// { -// "B": VariantMap( -// {}, -// ), -// "D": String( -// "tCnT", -// ), -// "C": VariantMap( -// { -// "j": String( -// "5:1", -// ), -// "x": String( -// "10:5", -// ), -// "f": String( -// "30:5", -// ), -// "F": String( -// "5:60", -// ), -// }, -// ), -// "A": VariantMap( -// {}, -// ), -// }, -// ), -// ], -// ), -// "UserModes": VariantList( -// [ -// VariantMap( -// { -// "audron": String( -// "o", -// ), -// "audron_": String( -// "", -// ), -// }, -// ), -// ], -// ), -// "name": VariantList( -// [ -// String( -// "#audron-test", -// ), -// ], -// ), -// }, -// ), -// }, -// ), -// ByteArray( -// "ServerList", -// ), -// VariantList( -// [ -// VariantMap( -// { -// "ProxyHost": String( -// "localhost", -// ), -// "sslVerify": bool( -// true, -// ), -// "UseSSL": bool( -// true, -// ), -// "Port": u32( -// 6697, -// ), -// "Password": String( -// "", -// ), -// "ProxyType": i32( -// 1, -// ), -// "sslVersion": i32( -// 0, -// ), -// "ProxyUser": String( -// "", -// ), -// "ProxyPass": String( -// "", -// ), -// "Host": String( -// "irc.snoonet.org", -// ), -// "ProxyPort": u32( -// 8080, -// ), -// "UseProxy": bool( -// false, -// ), -// }, -// ), -// ], -// ), -// ByteArray( -// "Supports", -// ), -// VariantMap( -// { -// "NAMESX": String( -// "", -// ), -// "MAXTARGETS": String( -// "20", -// ), -// "NICKLEN": String( -// "27", -// ), -// "SILENCE": String( -// "32", -// ), -// "PREFIX": String( -// "(Yohv)!@%+", -// ), -// "TOPICLEN": String( -// "307", -// ), -// "UHNAMES": String( -// "", -// ), -// "STATUSMSG": String( -// "!@%+", -// ), -// "SECURELIST": String( -// "60", -// ), -// "ACCEPT": String( -// "16", -// ), -// "CHANNELLEN": String( -// "64", -// ), -// "KEYLEN": String( -// "32", -// ), -// "EXTBAN": String( -// ",ABCNOQRSTUacjmprsz", -// ), -// "CHANMODES": String( -// "IXZbegw,k,FHJLWdfjlx,ABCDKMNOPQRSTcimnprstuz", -// ), -// "KICKLEN": String( -// "255", -// ), -// "ESILENCE": String( -// "CcdiNnPpTtx", -// ), -// "HOSTLEN": String( -// "64", -// ), -// "CHANLIMIT": String( -// "#:200", -// ), -// "USERMODES": String( -// ",,s,BHILRSTWcdghikorwx", -// ), -// "CLIENTTAGDENY": String( -// "*", -// ), -// "CHANTYPES": String( -// "#", -// ), -// "NETWORK": String( -// "Snoonet", -// ), -// "INVEX": String( -// "I", -// ), -// "ELIST": String( -// "CMNTU", -// ), -// "CALLERID": String( -// "g", -// ), -// "BOT": String( -// "B", -// ), -// "NAMELEN": String( -// "128", -// ), -// "USERIP": String( -// "", -// ), -// "MODES": String( -// "50", -// ), -// "WATCH": String( -// "30", -// ), -// "MAXLIST": String( -// "I:1024,X:1024,b:1024,e:1024,g:1024,w:1024", -// ), -// "USERLEN": String( -// "11", -// ), -// "CASEMAPPING": String( -// "rfc1459", -// ), -// "REMOVE": String( -// "", -// ), -// "LINELEN": String( -// "512", -// ), -// "AWAYLEN": String( -// "200", -// ), -// "EXCEPTS": String( -// "e", -// ), -// "WHOX": String( -// "", -// ), -// "SAFELIST": String( -// "", -// ), -// }, -// ), -// ByteArray( -// "autoIdentifyPassword", -// ), -// String( -// "", -// ), -// ByteArray( -// "autoIdentifyService", -// ), -// String( -// "NickServ", -// ), -// ByteArray( -// "autoReconnectInterval", -// ), -// u32( -// 60, -// ), -// ByteArray( -// "autoReconnectRetries", -// ), -// u16( -// 20, -// ), -// ByteArray( -// "codecForDecoding", -// ), -// ByteArray( -// "", -// ), -// ByteArray( -// "codecForEncoding", -// ), -// ByteArray( -// "", -// ), -// ByteArray( -// "codecForServer", -// ), -// ByteArray( -// "", -// ), -// ByteArray( -// "connectionState", -// ), -// i32( -// 3, -// ), -// ByteArray( -// "currentServer", -// ), -// String( -// "travincal.snoonet.org", -// ), -// ByteArray( -// "identityId", -// ), -// i32( -// 1, -// ), -// ByteArray( -// "isConnected", -// ), -// bool( -// true, -// ), -// ByteArray( -// "latency", -// ), -// i32( -// 13, -// ), -// ByteArray( -// "msgRateBurstSize", -// ), -// u32( -// 5, -// ), -// ByteArray( -// "msgRateMessageDelay", -// ), -// u32( -// 2200, -// ), -// ByteArray( -// "myNick", -// ), -// String( -// "audron_", -// ), -// ByteArray( -// "networkName", -// ), -// String( -// "snoonet", -// ), -// ByteArray( -// "perform", -// ), -// StringList( -// [ -// "", -// ], -// ), -// ByteArray( -// "rejoinChannels", -// ), -// bool( -// true, -// ), -// ByteArray( -// "saslAccount", -// ), -// String( -// "", -// ), -// ByteArray( -// "saslPassword", -// ), -// String( -// "", -// ), -// ByteArray( -// "unlimitedMessageRate", -// ), -// bool( -// false, -// ), -// ByteArray( -// "unlimitedReconnectRetries", -// ), -// bool( -// false, -// ), -// ByteArray( -// "useAutoIdentify", -// ), -// bool( -// false, -// ), -// ByteArray( -// "useAutoReconnect", -// ), -// bool( -// true, -// ), -// ByteArray( -// "useCustomMessageRate", -// ), -// bool( -// false, -// ), -// ByteArray( -// "useRandomServer", -// ), -// bool( -// false, -// ), -// ByteArray( -// "useSasl", -// ), -// bool( -// false, -// ), -// ], -// }, diff --git a/src/message/signalproxy/translation/mod.rs b/src/message/signalproxy/translation/mod.rs deleted file mode 100644 index a55d6f3..0000000 --- a/src/message/signalproxy/translation/mod.rs +++ /dev/null @@ -1,112 +0,0 @@ -/** -Quassel has 3 main ways to represent an object over the Network: - -# VariantList -The struct is serialized to a Vector of Variants. This is mostly used in the InitData messages. -First the field name as a ByteArray (UTF-8 String), followed by the field value in it's own Type's variant. -The order in which the fields are transmitted cannot be assumed. - -Example: -```ignore -NetworkConfig { - ping_timeout_enabled: true, - ping_interval: 0, -} -``` -to -```ignore -VariantList([ - ByteArray( - "pingTimeoutEnabled", - ), - bool( - true, - ), - ByteArray( - "pingInterval", - ), - i32( - 30, - ), -]) -``` - - -# VariantMap -The struct is represented as a `VariantMap`. The keys and values of -the struct are serialized to a corresponding `HashMap`. - -Example: -```ignore -NetworkConfig { - ping_timeout_enabled: false, - ping_interval: 0, -} -``` -to -```ignore -VariantMap({ - "pingTimeoutEnabled": bool(false) - "pingInterval": i32(0) -}) -``` - -## Structure of Arrays - -For Objects that are transmitted as multiple at once the VariantMap -representation is augemented and instead of transmitting multiple `VariantMaps`, -each field is a `VariantList` of Items. - -Example: -```ignore -vec![ - NetworkConfig { - ping_timeout_enabled: false, - ping_interval: 0, - }, - NetworkConfig { - ping_timeout_enabled: true, - ping_interval: 1, - }, -] -``` -to -```ignore -VariantMap({ - "pingTimeoutEnabled": VariantList([ - bool(false), - bool(true), - ]), - "pingInterval": VariantList([ - i32(0), - i32(1), - ]) -}) -``` -**/ -use crate::primitive::{Variant, VariantList}; - -#[deprecated(since="0.1.0", note="please use NetworkMap and NetworkList implementations")] -pub trait Network { - type Item; - - fn to_network(&self) -> Self::Item; - fn from_network(input: &mut Self::Item) -> Self; -} - -pub trait NetworkMap -where - // TODO correct this error type - Self::Item: TryFrom, - Self::Item: Into, -{ - type Item; - - fn to_network_map(&self) -> Self::Item; - fn from_network_map(input: &mut Self::Item) -> Self; -} - -pub trait NetworkList { - fn to_network_list(&self) -> VariantList; - fn from_network_list(input: &mut VariantList) -> Self; -} diff --git a/src/primitive/variant.rs b/src/primitive/variant.rs index d39e0bf..903f77c 100644 --- a/src/primitive/variant.rs +++ b/src/primitive/variant.rs @@ -88,7 +88,7 @@ impl From<&str> for Variant { } /// Implements the Network trait genericly for everything that -/// can be a VariantList / Vec +/// can be a [VariantList] impl crate::message::NetworkList for HashMap where T: std::convert::TryFrom + Into + Clone + std::hash::Hash + std::cmp::Eq, diff --git a/src/serialize.rs b/src/serialize.rs index 925b668..dffd0c3 100644 --- a/src/serialize.rs +++ b/src/serialize.rs @@ -1,3 +1,51 @@ +//! Traits for Serializing and Deserializing types to their byte representations +//! +//! example for a simple type would look like this with the `TYPE` usually fetched from [libquassel::primitive#constants] +//! +//! ```rust +//! use libquassel::{ProtocolError, serialize::*}; +//! +//! pub struct Example; +//! +//! impl Serialize for Example { +//! fn serialize(&self) -> Result, ProtocolError> { +//! // Serialize here +//! # todo!() +//! } +//! } +//! +//! impl Deserialize for Example { +//! fn parse(b: &[u8]) -> Result<(usize, Self), ProtocolError> { +//! // Deserialize here +//! # todo!() +//! } +//! } +//! +//! impl VariantType for Example { +//! // The QT Type identifier +//! const TYPE: u32 = 0x18; +//! } +//! ``` +//! +//! Some types are not serialized directly but are a [UserType]: +//! ```rust +//! use libquassel::{ProtocolError, serialize::*}; +//! +//! pub struct Example; +//! +//! impl Serialize for Example { +//! fn serialize(&self) -> Result, ProtocolError> { +//! // Serialize here +//! # todo!() +//! } +//! } +//! +//! impl UserType for Example { +//! const NAME: &str = "Example"; +//! } +//! ``` +//! [UserType]: libquassel::serialize::UserType + use crate::{ error::ProtocolError, primitive::{self, Variant}, @@ -64,13 +112,11 @@ pub trait VariantType { const TYPE: u32; } +/// Serialize a Type directly to it's Variant. +/// +/// Has a default implementation and is automaticly implemented for all types that +/// have a [VariantType] set and [Serialize] implemented. pub trait SerializeVariant: VariantType + Serialize { - /// Default implementation that passes the serialization through to [SerializeVariantInner]. - /// [SerializeVariantInner] is automaticly implemented for all types that implement [Serialize] - /// - /// ```rust ignore - /// self.serialize_variant_inner(Self::TYPE) - /// ``` fn serialize_variant(&self) -> Result, ProtocolError> { let mut res: Vec = Vec::new(); @@ -102,6 +148,14 @@ pub trait DeserializeUTF8 { Self: std::marker::Sized; } +/// Deserialize a Type for use in the Variant parsing. In opposite to [SerializeVariant] this does not deal +/// with the variant type itself as we have to match onto it genericly before passing it on to per Type +/// functions. +/// +/// Still using this gives us automatic implementations and more code reuse +/// +/// Has a default implementation and is automaticly implemented for all types that +/// have a [VariantType] set and [Deserialize] implemented. pub trait DeserializeVariant: VariantType + Deserialize where Variant: From, diff --git a/src/session.rs b/src/session.rs new file mode 100644 index 0000000..d4a788b --- /dev/null +++ b/src/session.rs @@ -0,0 +1,252 @@ +//! Generic Session handling implementation +//! +//! This module provides a relativly generic example implementation for handling the whole quassel session in +//! an application. This won't be usable in all cases but e.g. imediate mode GUIs can use it directly. + +use std::collections::HashMap; + +#[cfg(feature = "client")] +use crate::message::StatefulSyncableClient; +#[cfg(feature = "server")] +use crate::message::StatefulSyncableServer; + +use log::{debug, error, warn}; + +use crate::{ + message::{ + objects::{Types, *}, + Class, InitData, SessionInit, SyncMessage, Syncable, + }, + primitive::NetworkId, +}; + +/// Central struct that hold all our state +#[derive(Default, Debug)] +pub struct Session { + pub alias_manager: AliasManager, + pub buffer_syncer: BufferSyncer, + pub backlog_manager: BacklogManager, + pub buffer_view_manager: BufferViewManager, + pub cert_manager: CertManager, + pub core_info: CoreInfo, + pub highlight_rule_manager: HighlightRuleManager, + pub identities: Vec, + pub ignore_list_manager: IgnoreListManager, + pub networks: HashMap, + pub network_config: NetworkConfig, +} + +/// The Session Trait is the main point of entry and implements the basic logic +/// +/// Default implementations to handle sync and init of data structures is provided and you only have to +/// provide the getter functions. +pub trait SessionManager { + fn alias_manager(&mut self) -> &mut AliasManager; + fn buffer_syncer(&mut self) -> &mut BufferSyncer; + fn backlog_manager(&mut self) -> &mut BacklogManager; + fn buffer_view_manager(&mut self) -> &mut BufferViewManager; + fn cert_manager(&mut self) -> &mut CertManager; + fn core_info(&mut self) -> &mut CoreInfo; + fn highlight_rule_manager(&mut self) -> &mut HighlightRuleManager; + fn identities(&mut self) -> &mut Vec; + fn identity(&mut self, id: usize) -> Option<&mut Identity>; + fn ignore_list_manager(&mut self) -> &mut IgnoreListManager; + fn networks(&mut self) -> &mut HashMap; + fn network(&mut self, id: i32) -> Option<&mut Network>; + fn network_config(&mut self) -> &mut NetworkConfig; + + /// Handles an incoming [SyncMessage] and passes it on to the correct destination Object. + fn sync(&mut self, msg: SyncMessage) + where + Self: Sized, + { + match msg.class_name { + Class::AliasManager => self.alias_manager().sync(msg), + Class::BacklogManager => self.backlog_manager().sync(msg), + Class::BufferSyncer => self.buffer_syncer().sync(msg), + Class::BufferViewConfig => (), + Class::BufferViewManager => self.buffer_view_manager().sync(msg), + Class::CoreInfo => self.core_info().sync(msg), + // Synced via CoreInfo + Class::CoreData => (), + Class::HighlightRuleManager => self.highlight_rule_manager().sync(msg), + Class::Identity => { + let identity_id: i32 = msg.object_name.parse().unwrap(); + if let Some(identity) = self.identity(identity_id as usize) { + identity.sync(msg); + } else { + warn!("could not find identity with id: {:?}", identity_id); + } + } + Class::IgnoreListManager => self.ignore_list_manager().sync(msg), + Class::CertManager => self.cert_manager().sync(msg), + Class::Network => { + let id: i32 = msg.object_name.parse().unwrap(); + if let Some(network) = self.network(id) { + network.sync(msg) + } + } + // NetworkInfo does not receive SyncMessages directly but via Network + Class::NetworkInfo => (), + Class::NetworkConfig => match msg.object_name.as_ref() { + "GlobalNetworkConfig" => self.network_config().sync(msg), + _ => error!( + "received network config sync with unknown object name: {}", + msg.object_name + ), + }, + Class::IrcChannel => { + let mut object_name = msg.object_name.split('/'); + let network_id: i32 = object_name.next().unwrap().parse().unwrap(); + let channel = object_name.next().unwrap(); + + debug!("Syncing IrcChannel {} in Network {:?}", channel, network_id); + + if let Some(network) = self.network(network_id) { + if network.irc_channels.get_mut(channel).is_none() { + warn!( + "Could not find IrcChannel {} in Network {:?}", + channel, network_id + ) + } else { + match msg.slot_name.as_str() { + "addChannelMode" => { + let mut msg = msg.clone(); + let mode: char = get_param!(msg); + let mode_type: ChannelModeType = network.get_channel_mode_type(mode); + + network.irc_channels.get_mut(channel).unwrap().add_channel_mode( + mode_type, + mode, + get_param!(msg), + ); + } + "removeChannelMode" => { + let mut msg = msg.clone(); + let mode: char = get_param!(msg); + let mode_type: ChannelModeType = network.get_channel_mode_type(mode); + + network + .irc_channels + .get_mut(channel) + .unwrap() + .remove_channel_mode(mode_type, mode, get_param!(msg)); + } + _ => network.irc_channels.get_mut(channel).unwrap().sync(msg.clone()), + } + } + } else { + warn!("Could not find Network {:?}", network_id) + } + } + Class::IrcUser => { + let mut object_name = msg.object_name.split('/'); + let network_id: i32 = object_name.next().unwrap().parse().unwrap(); + let user = object_name.next().unwrap(); + + debug!("Syncing IrcUser {} in Network {:?}", user, network_id); + + if let Some(network) = self.network(network_id) { + if let Some(user) = network.irc_users.get_mut(user) { + user.sync(msg); + // TODO we probably need to deal with the quit here? + // and remove the user from the network + } else { + warn!("Could not find IrcUser {} in Network {:?}", user, network_id) + } + } else { + warn!("Could not find Network {:?}", network_id) + } + } + Class::Unknown => warn!("received unknown object syncmessage: {:?}", msg), + } + } + + fn session_init(&mut self, data: SessionInit) { + *self.identities() = data.identities; + } + + /// Handles an [InitData] messages and initializes the SessionManagers Objects. + /// TODO handle automatic sending of InitRequest for whatever objects will need that. + fn init(&mut self, data: InitData) { + match data.init_data { + Types::AliasManager(data) => self.alias_manager().init(data), + Types::BufferSyncer(data) => self.buffer_syncer().init(data), + Types::BufferViewConfig(data) => self.buffer_view_manager().init_buffer_view_config(data), + Types::BufferViewManager(data) => self.buffer_view_manager().init(data), + Types::CoreData(data) => self.core_info().set_core_data(data), + Types::HighlightRuleManager(data) => self.highlight_rule_manager().init(data), + Types::IgnoreListManager(data) => self.ignore_list_manager().init(data), + Types::CertManager(data) => self.cert_manager().init(data), + Types::Network(network) => { + let id: NetworkId = NetworkId(data.object_name.parse().unwrap()); + self.networks().insert(id, network); + } + Types::NetworkInfo(_) => (), + Types::NetworkConfig(_) => (), + Types::IrcChannel(channel) => { + let mut name = data.object_name.split("/"); + let id: i32 = name.next().unwrap().parse().unwrap(); + let name = name.next().unwrap(); + if let Some(network) = self.network(id) { + network.add_channel(name, channel) + } + } + Types::Unknown(_) => (), + } + } +} + +impl SessionManager for Session { + fn alias_manager(&mut self) -> &mut AliasManager { + &mut self.alias_manager + } + + fn buffer_syncer(&mut self) -> &mut BufferSyncer { + &mut self.buffer_syncer + } + + fn backlog_manager(&mut self) -> &mut BacklogManager { + &mut self.backlog_manager + } + + fn buffer_view_manager(&mut self) -> &mut BufferViewManager { + &mut self.buffer_view_manager + } + + fn cert_manager(&mut self) -> &mut CertManager { + &mut self.cert_manager + } + + fn core_info(&mut self) -> &mut CoreInfo { + &mut self.core_info + } + + fn highlight_rule_manager(&mut self) -> &mut HighlightRuleManager { + &mut self.highlight_rule_manager + } + + fn identities(&mut self) -> &mut Vec { + &mut self.identities + } + + fn identity(&mut self, id: usize) -> Option<&mut Identity> { + self.identities.get_mut(id) + } + + fn ignore_list_manager(&mut self) -> &mut IgnoreListManager { + &mut self.ignore_list_manager + } + + fn networks(&mut self) -> &mut HashMap { + &mut self.networks + } + + fn network(&mut self, id: i32) -> Option<&mut Network> { + self.networks.get_mut(&NetworkId(id)) + } + + fn network_config(&mut self) -> &mut NetworkConfig { + &mut self.network_config + } +} diff --git a/src/session/mod.rs b/src/session/mod.rs deleted file mode 100644 index 31842b1..0000000 --- a/src/session/mod.rs +++ /dev/null @@ -1,242 +0,0 @@ -use std::collections::HashMap; - -#[cfg(feature = "client")] -use crate::message::StatefulSyncableClient; -#[cfg(feature = "server")] -use crate::message::StatefulSyncableServer; - -use log::{debug, error, warn}; - -use crate::{ - message::{ - objects::{Types, *}, - Class, InitData, SessionInit, SyncMessage, Syncable, - }, - primitive::NetworkId, -}; - -// TODO implement nested types init and sync like BufferViewConfig in BufferViewManager - -#[derive(Default, Debug)] -pub struct Session { - pub alias_manager: AliasManager, - pub buffer_syncer: BufferSyncer, - pub backlog_manager: BacklogManager, - pub buffer_view_manager: BufferViewManager, - pub cert_manager: CertManager, - pub core_info: CoreInfo, - pub highlight_rule_manager: HighlightRuleManager, - pub identities: Vec, - pub ignore_list_manager: IgnoreListManager, - pub networks: HashMap, - pub network_config: NetworkConfig, -} - -/// The Session Trait is the main point of entry and implements the basic logic -pub trait SessionManager { - fn alias_manager(&mut self) -> &mut AliasManager; - fn buffer_syncer(&mut self) -> &mut BufferSyncer; - fn backlog_manager(&mut self) -> &mut BacklogManager; - fn buffer_view_manager(&mut self) -> &mut BufferViewManager; - fn cert_manager(&mut self) -> &mut CertManager; - fn core_info(&mut self) -> &mut CoreInfo; - fn highlight_rule_manager(&mut self) -> &mut HighlightRuleManager; - fn identities(&mut self) -> &mut Vec; - fn identity(&mut self, id: usize) -> Option<&mut Identity>; - fn ignore_list_manager(&mut self) -> &mut IgnoreListManager; - fn networks(&mut self) -> &mut HashMap; - fn network(&mut self, id: i32) -> Option<&mut Network>; - fn network_config(&mut self) -> &mut NetworkConfig; - - fn sync(&mut self, msg: SyncMessage) - where - Self: Sized, - { - match msg.class_name { - Class::AliasManager => self.alias_manager().sync(msg), - Class::BacklogManager => self.backlog_manager().sync(msg), - Class::BufferSyncer => self.buffer_syncer().sync(msg), - Class::BufferViewConfig => (), - Class::BufferViewManager => self.buffer_view_manager().sync(msg), - Class::CoreInfo => self.core_info().sync(msg), - // Synced via CoreInfo - Class::CoreData => (), - Class::HighlightRuleManager => self.highlight_rule_manager().sync(msg), - Class::Identity => { - let identity_id: i32 = msg.object_name.parse().unwrap(); - if let Some(identity) = self.identity(identity_id as usize) { - identity.sync(msg); - } else { - warn!("could not find identity with id: {:?}", identity_id); - } - } - Class::IgnoreListManager => self.ignore_list_manager().sync(msg), - Class::CertManager => self.cert_manager().sync(msg), - Class::Network => { - let id: i32 = msg.object_name.parse().unwrap(); - if let Some(network) = self.network(id) { - network.sync(msg) - } - } - // NetworkInfo does not receive SyncMessages directly but via Network - Class::NetworkInfo => (), - Class::NetworkConfig => match msg.object_name.as_ref() { - "GlobalNetworkConfig" => self.network_config().sync(msg), - _ => error!( - "received network config sync with unknown object name: {}", - msg.object_name - ), - }, - Class::IrcChannel => { - let mut object_name = msg.object_name.split('/'); - let network_id: i32 = object_name.next().unwrap().parse().unwrap(); - let channel = object_name.next().unwrap(); - - debug!("Syncing IrcChannel {} in Network {:?}", channel, network_id); - - if let Some(network) = self.network(network_id) { - if network.irc_channels.get_mut(channel).is_none() { - warn!( - "Could not find IrcChannel {} in Network {:?}", - channel, network_id - ) - } else { - match msg.slot_name.as_str() { - "addChannelMode" => { - let mut msg = msg.clone(); - let mode: char = get_param!(msg); - let mode_type: ChannelModeType = network.get_channel_mode_type(mode); - - network.irc_channels.get_mut(channel).unwrap().add_channel_mode( - mode_type, - mode, - get_param!(msg), - ); - } - "removeChannelMode" => { - let mut msg = msg.clone(); - let mode: char = get_param!(msg); - let mode_type: ChannelModeType = network.get_channel_mode_type(mode); - - network - .irc_channels - .get_mut(channel) - .unwrap() - .remove_channel_mode(mode_type, mode, get_param!(msg)); - } - _ => network.irc_channels.get_mut(channel).unwrap().sync(msg.clone()), - } - } - } else { - warn!("Could not find Network {:?}", network_id) - } - } - Class::IrcUser => { - let mut object_name = msg.object_name.split('/'); - let network_id: i32 = object_name.next().unwrap().parse().unwrap(); - let user = object_name.next().unwrap(); - - debug!("Syncing IrcUser {} in Network {:?}", user, network_id); - - if let Some(network) = self.network(network_id) { - if let Some(user) = network.irc_users.get_mut(user) { - user.sync(msg); - // TODO we probably need to deal with the quit here? - // and remove the user from the network - } else { - warn!("Could not find IrcUser {} in Network {:?}", user, network_id) - } - } else { - warn!("Could not find Network {:?}", network_id) - } - } - Class::Unknown => warn!("received unknown object syncmessage: {:?}", msg), - } - } - - fn session_init(&mut self, data: SessionInit) { - *self.identities() = data.identities; - } - - fn init(&mut self, data: InitData) { - match data.init_data { - Types::AliasManager(data) => self.alias_manager().init(data), - Types::BufferSyncer(data) => self.buffer_syncer().init(data), - Types::BufferViewConfig(data) => self.buffer_view_manager().init_buffer_view_config(data), - Types::BufferViewManager(data) => self.buffer_view_manager().init(data), - Types::CoreData(data) => self.core_info().set_core_data(data), - Types::HighlightRuleManager(data) => self.highlight_rule_manager().init(data), - Types::IgnoreListManager(data) => self.ignore_list_manager().init(data), - Types::CertManager(data) => self.cert_manager().init(data), - Types::Network(network) => { - let id: NetworkId = NetworkId(data.object_name.parse().unwrap()); - self.networks().insert(id, network); - } - Types::NetworkInfo(_) => (), - Types::NetworkConfig(_) => (), - Types::IrcChannel(channel) => { - let mut name = data.object_name.split("/"); - let id: i32 = name.next().unwrap().parse().unwrap(); - let name = name.next().unwrap(); - if let Some(network) = self.network(id) { - network.add_channel(name, channel) - } - } - Types::Unknown(_) => (), - } - } -} - -impl SessionManager for Session { - fn alias_manager(&mut self) -> &mut AliasManager { - &mut self.alias_manager - } - - fn buffer_syncer(&mut self) -> &mut BufferSyncer { - &mut self.buffer_syncer - } - - fn backlog_manager(&mut self) -> &mut BacklogManager { - &mut self.backlog_manager - } - - fn buffer_view_manager(&mut self) -> &mut BufferViewManager { - &mut self.buffer_view_manager - } - - fn cert_manager(&mut self) -> &mut CertManager { - &mut self.cert_manager - } - - fn core_info(&mut self) -> &mut CoreInfo { - &mut self.core_info - } - - fn highlight_rule_manager(&mut self) -> &mut HighlightRuleManager { - &mut self.highlight_rule_manager - } - - fn identities(&mut self) -> &mut Vec { - &mut self.identities - } - - fn identity(&mut self, id: usize) -> Option<&mut Identity> { - self.identities.get_mut(id) - } - - fn ignore_list_manager(&mut self) -> &mut IgnoreListManager { - &mut self.ignore_list_manager - } - - fn networks(&mut self) -> &mut HashMap { - &mut self.networks - } - - fn network(&mut self, id: i32) -> Option<&mut Network> { - self.networks.get_mut(&NetworkId(id)) - } - - fn network_config(&mut self) -> &mut NetworkConfig { - &mut self.network_config - } -} diff --git a/src/util.rs b/src/util.rs index 98e3521..eb5a595 100644 --- a/src/util.rs +++ b/src/util.rs @@ -38,6 +38,17 @@ pub fn insert_bytes(pos: usize, buf: &mut Vec, input: &mut [u8]) { } } +/// Easily create HashMaps for tests +/// +/// ```rust +/// use libquassel::{map, s}; +/// use libquassel::primitive::{Variant, VariantMap}; +/// +/// let example: VariantMap = map! { +/// s!("id") => Variant::VariantList(vec![Variant::i32(1)]), +/// s!("name") => Variant::StringList(vec![s!("testrule")]), +///}; +/// ``` #[macro_export] macro_rules! map { // map-like @@ -50,6 +61,7 @@ macro_rules! map { }; } +/// Shorthand to make an owned string: `s!("example text")` #[macro_export] macro_rules! s { ($values:expr) => { @@ -57,6 +69,7 @@ macro_rules! s { }; } +/// Remove the first entry in a SyncMessage #[macro_export] macro_rules! get_param { ( $msg:expr ) => { -- cgit v1.2.3