aboutsummaryrefslogtreecommitdiff
path: root/src/message
diff options
context:
space:
mode:
authorMax Audron <audron@cocaine.farm>2025-02-23 17:04:26 +0100
committerMax Audron <audron@cocaine.farm>2025-02-23 17:04:26 +0100
commitb630f1855ec194703bfb0fdac22de9e18eaa5237 (patch)
tree7abe6fda0d02c43fc3da6aa296850a6cd8e4c3e6 /src/message
parentclean up unused_import and unused_variables a bit (diff)
add basic network syncables
Diffstat (limited to 'src/message')
-rw-r--r--src/message/signalproxy/objects/highlightrulemanager.rs3
-rw-r--r--src/message/signalproxy/objects/ircuser.rs2
-rw-r--r--src/message/signalproxy/objects/network.rs361
-rw-r--r--src/message/signalproxy/objects/networkinfo.rs39
4 files changed, 388 insertions, 17 deletions
diff --git a/src/message/signalproxy/objects/highlightrulemanager.rs b/src/message/signalproxy/objects/highlightrulemanager.rs
index abcc2c5..9083686 100644
--- a/src/message/signalproxy/objects/highlightrulemanager.rs
+++ b/src/message/signalproxy/objects/highlightrulemanager.rs
@@ -10,9 +10,6 @@ use crate::message::StatefulSyncableServer;
use crate::message::Syncable;
use crate::primitive::Variant;
-#[cfg(feature = "server")]
-use crate::message::signalproxy::translation::NetworkMap;
-
#[derive(Default, Debug, Clone, PartialEq, NetworkList, NetworkMap)]
pub struct HighlightRuleManager {
#[network(rename = "HighlightRuleList", variant = "VariantMap", network = "map")]
diff --git a/src/message/signalproxy/objects/ircuser.rs b/src/message/signalproxy/objects/ircuser.rs
index 93ae013..72f98be 100644
--- a/src/message/signalproxy/objects/ircuser.rs
+++ b/src/message/signalproxy/objects/ircuser.rs
@@ -65,7 +65,7 @@ impl IrcUser {
sync!("removeUserModes", [modes]);
}
- pub fn update_hostmask(&mut self, mask: String) {}
+ pub fn update_hostmask(&mut self, _mask: String) {}
pub fn join_channel(&mut self, channel: String) {
if !self.channels.contains(&channel) {
diff --git a/src/message/signalproxy/objects/network.rs b/src/message/signalproxy/objects/network.rs
index 33d2b33..a8a882d 100644
--- a/src/message/signalproxy/objects/network.rs
+++ b/src/message/signalproxy/objects/network.rs
@@ -1,30 +1,44 @@
use std::collections::HashMap;
+use itertools::Itertools;
+use log::{error, warn};
use num_derive::{FromPrimitive, ToPrimitive};
-use num_traits::FromPrimitive;
+use num_traits::{FromPrimitive, ToPrimitive};
-use libquassel_derive::{NetworkList, NetworkMap};
+use libquassel_derive::{sync, NetworkList, NetworkMap, Setters};
+use crate::error::ProtocolError;
use crate::message::signalproxy::translation::NetworkMap;
+use crate::message::{Class, Syncable};
use crate::primitive::{Variant, VariantList, VariantMap};
use super::{ircchannel::IrcChannel, ircuser::IrcUser, networkinfo::NetworkInfo};
-#[derive(Default, Debug, Clone, PartialEq)]
+#[derive(Default, Debug, Clone, PartialEq, Setters)]
pub struct Network {
pub my_nick: String,
pub latency: i32,
pub current_server: String,
+ #[setter(name = "connected")]
pub is_connected: bool,
pub connection_state: ConnectionState,
+ #[setter(skip)]
pub prefixes: Vec<char>,
+ #[setter(skip)]
pub prefix_modes: Vec<char>,
+ #[setter(skip)]
pub channel_modes: HashMap<ChannelModeType, String>,
+ #[setter(skip)]
pub irc_users: HashMap<String, IrcUser>,
+ #[setter(skip)]
pub irc_channels: HashMap<String, IrcChannel>,
+ #[setter(skip)]
pub supports: HashMap<String, String>,
+ #[setter(skip)]
pub caps: HashMap<String, String>,
+ #[setter(skip)]
pub caps_enabled: Vec<String>,
+ #[setter(skip)]
pub network_info: NetworkInfo,
}
@@ -78,6 +92,189 @@ impl Network {
pub fn add_channel(&mut self, name: &str, channel: IrcChannel) {
self.irc_channels.insert(name.to_owned(), channel);
}
+
+ pub fn connect(&self) {
+ #[cfg(feature = "client")]
+ sync!("requestConnect", [])
+ }
+
+ pub fn disconnect(&self) {
+ #[cfg(feature = "client")]
+ sync!("requestDisconnect", [])
+ }
+
+ pub fn set_network_info(&mut self, network_info: NetworkInfo) {
+ #[cfg(feature = "client")]
+ sync!("requestSetNetworkInfo", [network_info.to_network_map()]);
+
+ self.network_info = network_info;
+ }
+
+ /// Enable the capability `cap` if it is not already enabled
+ pub fn acknowledge_cap(&mut self, cap: String) {
+ #[cfg(feature = "server")]
+ sync!("acknowledgeCap", [cap.clone()]);
+
+ if !self.caps_enabled.contains(&cap) {
+ self.caps_enabled.push(cap);
+ } else {
+ warn!("Capability {} already enabled", cap)
+ }
+ }
+
+ /// Add a new capability supported by the server
+ pub fn add_cap(&mut self, cap: String, value: String) {
+ #[cfg(feature = "server")]
+ sync!("addCap", [cap.clone(), value.clone()]);
+
+ self.caps.insert(cap, value);
+ }
+
+ /// Clear `caps` and `caps_enabled`
+ pub fn clear_caps(&mut self) {
+ #[cfg(feature = "server")]
+ sync!("clearCaps", []);
+
+ self.caps.clear();
+ self.caps_enabled.clear();
+ }
+
+ /// Remove a capability from `caps` and `caps_enabled`
+ pub fn remove_cap(&mut self, cap: String) {
+ #[cfg(feature = "server")]
+ sync!("removeCap", [cap.clone()]);
+
+ self.caps.remove(&cap);
+ if let Some((i, _)) = self.caps_enabled.iter().find_position(|c| **c == cap) {
+ self.caps_enabled.remove(i);
+ }
+ }
+
+ // TODO
+ pub fn add_irc_channel(&mut self, _name: String) {}
+ pub fn add_irc_user(&mut self, _hostmask: String) {}
+
+ pub fn add_support(&mut self, key: String, value: String) {
+ #[cfg(feature = "server")]
+ sync!("addSupport", [key.clone(), value.clone()]);
+
+ self.supports.insert(key, value);
+ }
+
+ pub fn remove_support(&mut self, key: String) {
+ #[cfg(feature = "server")]
+ sync!("removeSupport", [key.clone()]);
+
+ self.supports.remove(&key);
+ }
+
+ pub fn emit_connection_error(&mut self, error: String) {
+ #[cfg(feature = "server")]
+ sync!("emitConnectionError", [error.clone()]);
+
+ error!("{}", error)
+ }
+
+ /// Rename the user object in the network object
+ /// TODO the actual nick change is done with a sepperate sync message against the IrcUser object?
+ pub fn irc_user_nick_changed(&mut self, before: String, after: String) {
+ #[cfg(feature = "server")]
+ sync!("ircUserNickChanged", [before.clone(), after.clone()]);
+
+ if let Some(user) = self.irc_users.remove(&before) {
+ self.irc_users.insert(after, user);
+ } else {
+ warn!("irc user {} not found", before);
+ }
+ }
+}
+
+impl Syncable for Network {
+ const CLASS: Class = Class::Network;
+}
+
+#[cfg(feature = "client")]
+impl crate::message::StatefulSyncableClient for Network {
+ fn sync_custom(&mut self, mut msg: crate::message::SyncMessage)
+ where
+ Self: Sized,
+ {
+ match msg.slot_name.as_str() {
+ "acknowledgeCap" => self.acknowledge_cap(get_param!(msg)),
+ "addCap" => self.add_cap(get_param!(msg), get_param!(msg)),
+ "addIrcChannel" => self.add_irc_channel(get_param!(msg)),
+ "addIrcUser" => self.add_irc_user(get_param!(msg)),
+ "addSupport" => self.add_support(get_param!(msg), get_param!(msg)),
+ "clearCaps" => self.clear_caps(),
+ "emitConnectionError" => self.emit_connection_error(get_param!(msg)),
+ "ircUserNickChanged" => self.irc_user_nick_changed(get_param!(msg), get_param!(msg)),
+ "removeCap" => self.remove_cap(get_param!(msg)),
+ "removeSupport" => self.remove_support(get_param!(msg)),
+ "setAutoIdentifyPassword" => self.network_info.set_auto_identify_password(get_param!(msg)),
+ "setAutoIdentifyService" => self.network_info.set_auto_identify_service(get_param!(msg)),
+ "setAutoReconnectInterval" => self.network_info.set_auto_reconnect_interval(get_param!(msg)),
+ "setAutoReconnectRetries" => self.network_info.set_auto_reconnect_retries(get_param!(msg)),
+ "setCodecForDecoding" => self.network_info.set_codec_for_decoding(get_param!(msg)),
+ "setCodecForEncoding" => self.network_info.set_codec_for_encoding(get_param!(msg)),
+ "setCodecForServer" => self.network_info.set_codec_for_server(get_param!(msg)),
+ "setConnected" => self.set_connected(get_param!(msg)),
+ "setConnectionState" => self.set_connection_state(get_param!(msg)),
+ "setCurrentServer" => self.set_current_server(get_param!(msg)),
+ "setIdentity" => self.network_info.set_identity_id(get_param!(msg)),
+ "setLatency" => self.set_latency(get_param!(msg)),
+ "setMessageRateBurstSize" => self.network_info.set_msg_rate_burst_size(get_param!(msg)),
+ "setMessageRateDelay" => self.network_info.set_msg_rate_message_delay(get_param!(msg)),
+ "setMyNick" => self.set_my_nick(get_param!(msg)),
+ "setNetworkName" => self.network_info.set_network_name(get_param!(msg)),
+ "setNetworkInfo" => self.set_network_info(NetworkInfo::from_network_map(
+ &mut VariantMap::try_from(msg.params.remove(0)).unwrap(),
+ )),
+ "setPerform" => self.network_info.set_perform(get_param!(msg)),
+ "setRejoinChannels" => self.network_info.set_rejoin_channels(get_param!(msg)),
+ "setSaslAccount" => self.network_info.set_sasl_account(get_param!(msg)),
+ "setSaslPassword" => self.network_info.set_sasl_password(get_param!(msg)),
+ // "setServerList" => self.network_info.set_server_list(get_param!(msg)),
+ "setActualServerList" => self.network_info.set_server_list({
+ match msg.params.remove(0) {
+ Variant::VariantList(mut variants) => {
+ Vec::<NetworkServer>::from_network_map(&mut variants)
+ }
+ _ => {
+ error!("{}", ProtocolError::WrongVariant);
+ // TODO FIXME
+ Vec::new()
+ }
+ }
+ }),
+ "setUnlimitedMessageRate" => self.network_info.set_unlimited_message_rate(get_param!(msg)),
+ "setUnlimitedReconnectRetries" => {
+ self.network_info.set_unlimited_reconnect_retries(get_param!(msg))
+ }
+ "setUseAutoIdentify" => self.network_info.set_use_auto_identify(get_param!(msg)),
+ "setUseAutoReconnect" => self.network_info.set_use_auto_reconnect(get_param!(msg)),
+ "setUseCustomMessageRate" => self.network_info.set_use_custom_message_rate(get_param!(msg)),
+ "setUseRandomServer" => self.network_info.set_use_random_server(get_param!(msg)),
+ "setUseSasl" => self.network_info.set_use_sasl(get_param!(msg)),
+ _ => (),
+ }
+ }
+}
+
+#[cfg(feature = "server")]
+impl crate::message::StatefulSyncableServer for Network {
+ fn sync_custom(&mut self, mut msg: crate::message::SyncMessage)
+ where
+ Self: Sized,
+ {
+ match msg.slot_name.as_str() {
+ "requestConnect" => self.connect(),
+ "requestDisconnect" => self.disconnect(),
+ "requestSetNetworkInfo" => self.set_network_info(NetworkInfo::from_network_map(
+ &mut VariantMap::try_from(msg.params.remove(0)).unwrap(),
+ )),
+ _ => (),
+ }
+ }
}
impl crate::message::signalproxy::NetworkList for Network {
@@ -93,7 +290,7 @@ impl crate::message::signalproxy::NetworkList for Network {
res.push(Variant::ByteArray(s!("isConnected")));
res.push(Variant::bool(self.is_connected));
res.push(Variant::ByteArray(s!("connectionState")));
- res.push(Variant::i32(self.connection_state.clone() as i32));
+ res.push(Variant::i32(self.connection_state as i32));
res.push(Variant::ByteArray(s!("Supports")));
res.push(Variant::VariantMap(
@@ -254,6 +451,143 @@ impl crate::message::signalproxy::NetworkList for Network {
}
}
+impl crate::message::signalproxy::NetworkMap for Network {
+ type Item = VariantMap;
+
+ fn to_network_map(&self) -> Self::Item {
+ let mut res = VariantMap::new();
+
+ res.insert("myNick".to_owned(), Variant::String(self.my_nick.clone()));
+ res.insert("latency".to_owned(), Variant::i32(self.latency));
+ res.insert(
+ "currentServer".to_owned(),
+ Variant::String(self.current_server.clone()),
+ );
+ res.insert("isConnected".to_owned(), Variant::bool(self.is_connected));
+ res.insert(
+ "connectionState".to_owned(),
+ Variant::i32(self.connection_state as i32),
+ );
+
+ res.insert(
+ "Supports".to_owned(),
+ Variant::VariantMap(
+ self.supports
+ .iter()
+ .map(|(k, v)| (k.clone(), Variant::String(v.clone())))
+ .collect(),
+ ),
+ );
+
+ res.insert(
+ "Caps".to_owned(),
+ Variant::VariantMap(
+ self.caps
+ .iter()
+ .map(|(k, v)| (k.clone(), Variant::String(v.clone())))
+ .collect(),
+ ),
+ );
+
+ res.insert(
+ "CapsEnabled".to_owned(),
+ Variant::VariantList(
+ self.caps_enabled
+ .iter()
+ .map(|v| Variant::String(v.clone()))
+ .collect(),
+ ),
+ );
+
+ res.insert(s!("IrcUsersAndChannels"), {
+ let mut map = VariantMap::new();
+
+ map.insert(
+ s!("Users"),
+ Variant::VariantMap(self.irc_users.iter().fold(HashMap::new(), |mut res, (_, v)| {
+ res.extend(v.to_network_map());
+
+ res
+ })),
+ );
+
+ let channels = self.irc_channels.iter().fold(HashMap::new(), |mut res, (_, v)| {
+ res.extend(v.to_network_map());
+
+ res
+ });
+
+ map.insert(s!("Channels"), Variant::VariantMap(channels));
+
+ Variant::VariantMap(map)
+ });
+
+ res.extend(self.network_info.to_network_map());
+
+ return res;
+ }
+
+ fn from_network_map(input: &mut Self::Item) -> Self {
+ let users_and_channels: VariantMap =
+ { input.get("IrcUsersAndChannels").unwrap().try_into().unwrap() };
+
+ return Self {
+ my_nick: input.get("myNick").unwrap().into(),
+ latency: input.get("latency").unwrap().try_into().unwrap(),
+ current_server: input.get("currentServer").unwrap().into(),
+ is_connected: input.get("isConnected").unwrap().try_into().unwrap(),
+ connection_state: ConnectionState::from_i32(
+ input.get("connectionState").unwrap().try_into().unwrap(),
+ )
+ .unwrap(),
+ prefixes: Vec::new(),
+ prefix_modes: Vec::new(),
+ channel_modes: HashMap::with_capacity(4),
+ irc_users: {
+ match users_and_channels.get("Users") {
+ Some(users) => {
+ let users: Vec<IrcUser> = Vec::<IrcUser>::from_network_map(
+ &mut users.try_into().expect("failed to convert Users"),
+ );
+
+ users.into_iter().map(|user| (user.nick.clone(), user)).collect()
+ }
+ None => HashMap::new(),
+ }
+ },
+ irc_channels: {
+ match users_and_channels.get("Channels") {
+ Some(channels) => {
+ let channels: Vec<IrcChannel> =
+ Vec::<IrcChannel>::from_network_map(&mut channels.try_into().unwrap());
+ channels
+ .into_iter()
+ .map(|channel| (channel.name.clone(), channel))
+ .collect()
+ }
+ None => HashMap::new(),
+ }
+ },
+ supports: VariantMap::try_from(input.get("Supports").unwrap())
+ .unwrap()
+ .into_iter()
+ .map(|(k, v)| (k, v.into()))
+ .collect(),
+ caps: VariantMap::try_from(input.get("Caps").unwrap())
+ .unwrap()
+ .into_iter()
+ .map(|(k, v)| (k, v.into()))
+ .collect(),
+ caps_enabled: VariantList::try_from(input.get("CapsEnabled").unwrap())
+ .unwrap()
+ .into_iter()
+ .map(|v| v.into())
+ .collect(),
+ network_info: NetworkInfo::from_network_map(input),
+ };
+ }
+}
+
#[derive(Debug, Clone, PartialEq, NetworkMap)]
pub struct NetworkServer {
#[network(rename = "Host")]
@@ -435,7 +769,7 @@ mod tests {
}
#[allow(dead_code)]
-#[derive(Debug, Clone, PartialEq, FromPrimitive, ToPrimitive)]
+#[derive(Debug, Clone, Copy, PartialEq, FromPrimitive, ToPrimitive)]
#[repr(C)]
pub enum ConnectionState {
Disconnected = 0x00,
@@ -452,6 +786,23 @@ impl Default for ConnectionState {
}
}
+impl Into<Variant> for ConnectionState {
+ fn into(self) -> Variant {
+ Variant::i32(self.to_i32().unwrap())
+ }
+}
+
+impl TryFrom<Variant> for ConnectionState {
+ type Error = ProtocolError;
+
+ fn try_from(value: Variant) -> Result<Self, Self::Error> {
+ match value {
+ Variant::i32(n) => Ok(ConnectionState::from_i32(n).unwrap()),
+ _ => Err(ProtocolError::WrongVariant),
+ }
+ }
+}
+
#[allow(dead_code)]
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, FromPrimitive, ToPrimitive)]
#[repr(C)]
diff --git a/src/message/signalproxy/objects/networkinfo.rs b/src/message/signalproxy/objects/networkinfo.rs
index d11b7c8..11a4d0f 100644
--- a/src/message/signalproxy/objects/networkinfo.rs
+++ b/src/message/signalproxy/objects/networkinfo.rs
@@ -1,14 +1,18 @@
-use crate::primitive::StringList;
+use crate::{
+ message::{Class, Syncable},
+ primitive::StringList,
+};
-use libquassel_derive::NetworkList;
+use libquassel_derive::{NetworkList, NetworkMap, Setters};
use crate::message::objects::network::NetworkServer;
-#[derive(Default, Debug, Clone, PartialEq, NetworkList)]
+#[derive(Default, Debug, Clone, PartialEq, NetworkList, NetworkMap, Setters)]
pub struct NetworkInfo {
#[network(rename = "networkName")]
pub network_name: String,
+ #[setter(skip)]
#[network(rename = "ServerList", variant = "VariantList", network = "map")]
pub server_list: Vec<NetworkServer>,
#[network(rename = "perform")]
@@ -34,7 +38,8 @@ pub struct NetworkInfo {
// TODO add these type aliases or usertypes in variants
// pub network_id: NetworkId,
- // pub identity_id: IdentityId,
+ #[network(rename = "identityId")]
+ pub identity_id: i32,
#[network(rename = "msgRateBurstSize")]
pub msg_rate_burst_size: u32,
#[network(rename = "msgRateMessageDelay")]
@@ -65,6 +70,24 @@ pub struct NetworkInfo {
// pub auto_away_active: bool,
}
+impl NetworkInfo {
+ pub fn set_server_list(&mut self, servers: Vec<NetworkServer>) {
+ #[cfg(feature = "server")]
+ {
+ use crate::message::NetworkMap;
+ use libquassel_derive::sync;
+
+ sync!("setServerList", [Vec::<NetworkServer>::to_network_map(&servers)]);
+ }
+
+ self.server_list = servers;
+ }
+}
+
+impl Syncable for NetworkInfo {
+ const CLASS: Class = Class::Network;
+}
+
#[cfg(test)]
mod tests {
use crate::primitive::{Variant, VariantList};
@@ -96,6 +119,8 @@ mod tests {
Variant::ByteArray(s!("")),
Variant::ByteArray(s!("codecForDecoding")),
Variant::ByteArray(s!("")),
+ Variant::ByteArray(s!("identityId")),
+ Variant::i32(0),
Variant::ByteArray(s!("msgRateBurstSize")),
Variant::u32(5),
Variant::ByteArray(s!("msgRateMessageDelay")),
@@ -125,6 +150,7 @@ mod tests {
fn get_runtime() -> NetworkInfo {
NetworkInfo {
+ identity_id: 0,
network_name: s!("snoonet"),
server_list: vec![],
perform: vec![s!("")],
@@ -158,9 +184,6 @@ mod tests {
#[test]
fn networkinfo_from_network() {
- assert_eq!(
- NetworkInfo::from_network_list(&mut get_network()),
- get_runtime()
- )
+ assert_eq!(NetworkInfo::from_network_list(&mut get_network()), get_runtime())
}
}