diff options
| author | Gustav Sörnäs <gustav@sornas.net> | 2020-10-21 19:40:28 +0200 |
|---|---|---|
| committer | Gustav Sörnäs <gustav@sornas.net> | 2020-10-21 19:40:28 +0200 |
| commit | 161f58f3e6a0088fa2612d9af54c7b2939be37e7 (patch) | |
| tree | 78d3532e46525db78a48b5f8bcd27ebdac693f6e /mumd/src/state | |
| parent | 92d64a493ced079645e7f0601ff9a573c1616203 (diff) | |
| parent | 962af6bd39f77381aeca874a40fa0b2fb36f33c4 (diff) | |
| download | mum-161f58f3e6a0088fa2612d9af54c7b2939be37e7.tar.gz | |
Merge remote-tracking branch 'origin/refactor-state' into main
Diffstat (limited to 'mumd/src/state')
| -rw-r--r-- | mumd/src/state/channel.rs | 184 | ||||
| -rw-r--r-- | mumd/src/state/server.rs | 123 | ||||
| -rw-r--r-- | mumd/src/state/user.rs | 145 |
3 files changed, 452 insertions, 0 deletions
diff --git a/mumd/src/state/channel.rs b/mumd/src/state/channel.rs new file mode 100644 index 0000000..8bbf919 --- /dev/null +++ b/mumd/src/state/channel.rs @@ -0,0 +1,184 @@ +use crate::state::user::User; + +use mumble_protocol::control::msgs; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Channel { + description: Option<String>, + links: Vec<u32>, + max_users: u32, + name: String, + parent: Option<u32>, + position: i32, +} + +impl Channel { + pub fn new(mut msg: msgs::ChannelState) -> Self { + Self { + description: if msg.has_description() { + Some(msg.take_description()) + } else { + None + }, + links: Vec::new(), + max_users: msg.get_max_users(), + name: msg.take_name(), + parent: if msg.has_parent() { + Some(msg.get_parent()) + } else { + None + }, + position: msg.get_position(), + } + } + + pub fn parse_channel_state(&mut self, mut msg: msgs::ChannelState) { + if msg.has_description() { + self.description = Some(msg.take_description()); + } + self.links = msg.take_links(); + if msg.has_max_users() { + self.max_users = msg.get_max_users(); + } + if msg.has_name() { + self.name = msg.take_name(); + } + if msg.has_parent() { + self.parent = Some(msg.get_parent()); + } + if msg.has_position() { + self.position = msg.get_position(); + } + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn path(&self, channels: &HashMap<u32, Channel>) -> String { + match &self.parent { + Some(t) => format!("{}/{}", channels.get(t).unwrap().path(channels), self.name), + None => self.name.clone(), + } + } +} + +#[derive(Debug)] +struct ProtoTree<'a> { + channel: Option<&'a Channel>, + children: HashMap<u32, ProtoTree<'a>>, + users: Vec<&'a User>, +} + +impl<'a> ProtoTree<'a> { + fn walk_and_add( + &mut self, + channel: &'a Channel, + users: &HashMap<u32, Vec<&'a User>>, + walk: &[u32], + ) { + match walk { + [] => unreachable!("Walks should always have at least one element"), + &[node] => { + let pt = self.children.entry(node).or_insert(ProtoTree { + channel: None, + children: HashMap::new(), + users: Vec::new(), + }); + pt.channel = Some(channel); + pt.users = users.get(&node).map(|e| e.clone()).unwrap_or(Vec::new()); + } + longer => { + self.children + .entry(longer[0]) + .or_insert(ProtoTree { + channel: None, + children: HashMap::new(), + users: Vec::new(), + }) + .walk_and_add(channel, users, &walk[1..]); + } + } + } +} + +impl<'a> From<&ProtoTree<'a>> for mumlib::state::Channel { + fn from(tree: &ProtoTree<'a>) -> Self { + let mut channel = mumlib::state::Channel::from(tree.channel.unwrap()); + let mut children = tree + .children + .iter() + .map(|e| { + ( + e.1.channel.unwrap().position, + mumlib::state::Channel::from(e.1), + ) + }) + .collect::<Vec<_>>(); + children.sort_by_key(|e| (e.0, e.1.name.clone())); + channel.children = children.into_iter().map(|e| e.1).collect(); + channel.users = tree.users.iter().map(|e| (*e).into()).collect(); + channel + } +} + +pub fn into_channel( + channels: &HashMap<u32, Channel>, + users: &HashMap<u32, User>, +) -> mumlib::state::Channel { + let mut walks = Vec::new(); + + let mut channel_lookup = HashMap::new(); + + for user in users.values() { + channel_lookup + .entry(user.channel()) + .or_insert(Vec::new()) + .push(user); + } + + for (channel_id, channel) in channels { + let mut walk = Vec::new(); + let mut current = *channel_id; + while let Some(next) = channels.get(¤t).unwrap().parent { + walk.push(current); + current = next; + } + walk.reverse(); + + if walk.len() > 0 { + walks.push((walk, channel)); + } + } + + //root node is ignored because of how walk_and_add is implemented on ProtoTree + let mut proto_tree = ProtoTree { + channel: Some(channels.get(&0).unwrap()), + children: HashMap::new(), + users: channel_lookup + .get(&0) + .map(|e| e.clone()) + .unwrap_or(Vec::new()), + }; + + for (walk, channel) in walks { + proto_tree.walk_and_add(channel, &channel_lookup, &walk); + } + + (&proto_tree).into() +} + +impl From<&Channel> for mumlib::state::Channel { + fn from(channel: &Channel) -> Self { + mumlib::state::Channel { + description: channel.description.clone(), + links: Vec::new(), + max_users: channel.max_users, + name: channel.name.clone(), + children: Vec::new(), + users: Vec::new(), + } + } +} diff --git a/mumd/src/state/server.rs b/mumd/src/state/server.rs new file mode 100644 index 0000000..b99c7e6 --- /dev/null +++ b/mumd/src/state/server.rs @@ -0,0 +1,123 @@ +use crate::state::channel::{into_channel, Channel}; +use crate::state::user::User; + +use log::*; +use mumble_protocol::control::msgs; +use serde::{Deserialize, Serialize}; +use std::collections::hash_map::Entry; +use std::collections::HashMap; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Server { + channels: HashMap<u32, Channel>, + users: HashMap<u32, User>, + pub welcome_text: Option<String>, + + username: Option<String>, + session_id: Option<u32>, + + host: Option<String>, +} + +impl Server { + pub fn new() -> Self { + Self { + channels: HashMap::new(), + users: HashMap::new(), + welcome_text: None, + username: None, + session_id: None, + host: None, + } + } + + pub fn parse_server_sync(&mut self, mut msg: msgs::ServerSync) { + if msg.has_welcome_text() { + self.welcome_text = Some(msg.take_welcome_text()); + } + } + + pub fn parse_channel_state(&mut self, msg: msgs::ChannelState) { + if !msg.has_channel_id() { + warn!("Can't parse channel state without channel id"); + return; + } + match self.channels.entry(msg.get_channel_id()) { + Entry::Vacant(e) => { + e.insert(Channel::new(msg)); + } + Entry::Occupied(mut e) => e.get_mut().parse_channel_state(msg), + } + } + + pub fn parse_channel_remove(&mut self, msg: msgs::ChannelRemove) { + if !msg.has_channel_id() { + warn!("Can't parse channel remove without channel id"); + return; + } + match self.channels.entry(msg.get_channel_id()) { + Entry::Vacant(_) => { + warn!("Attempted to remove channel that doesn't exist"); + } + Entry::Occupied(e) => { + e.remove(); + } + } + } + + pub fn parse_user_state(&mut self, msg: msgs::UserState) { + if !msg.has_session() { + warn!("Can't parse user state without session"); + return; + } + match self.users.entry(msg.get_session()) { + Entry::Vacant(e) => { + e.insert(User::new(msg)); + } + Entry::Occupied(mut e) => e.get_mut().parse_user_state(msg), + } + } + + pub fn channels(&self) -> &HashMap<u32, Channel> { + &self.channels + } + + pub fn host_mut(&mut self) -> &mut Option<String> { + &mut self.host + } + + pub fn session_id(&self) -> Option<u32> { + self.session_id + } + + pub fn session_id_mut(&mut self) -> &mut Option<u32> { + &mut self.session_id + } + + pub fn users(&self) -> &HashMap<u32, User> { + &self.users + } + + pub fn users_mut(&mut self) -> &mut HashMap<u32, User> { + &mut self.users + } + + pub fn username(&self) -> Option<&str> { + self.username.as_ref().map(|e| e.as_str()) + } + + pub fn username_mut(&mut self) -> &mut Option<String> { + &mut self.username + } +} + +impl From<&Server> for mumlib::state::Server { + fn from(server: &Server) -> Self { + mumlib::state::Server { + channels: into_channel(server.channels(), server.users()), + welcome_text: server.welcome_text.clone(), + username: server.username.clone().unwrap(), + host: server.host.as_ref().unwrap().clone(), + } + } +} diff --git a/mumd/src/state/user.rs b/mumd/src/state/user.rs new file mode 100644 index 0000000..679d0ff --- /dev/null +++ b/mumd/src/state/user.rs @@ -0,0 +1,145 @@ +use log::*; +use mumble_protocol::control::msgs; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct User { + channel: u32, + comment: Option<String>, + hash: Option<String>, + name: String, + priority_speaker: bool, + recording: bool, + + suppress: bool, // by me + self_mute: bool, // by self + self_deaf: bool, // by self + mute: bool, // by admin + deaf: bool, // by admin +} + +impl User { + pub fn new(mut msg: msgs::UserState) -> Self { + Self { + channel: msg.get_channel_id(), + comment: if msg.has_comment() { + Some(msg.take_comment()) + } else { + None + }, + hash: if msg.has_hash() { + Some(msg.take_hash()) + } else { + None + }, + name: msg.take_name(), + priority_speaker: msg.has_priority_speaker() && msg.get_priority_speaker(), + recording: msg.has_recording() && msg.get_recording(), + suppress: msg.has_suppress() && msg.get_suppress(), + self_mute: msg.has_self_mute() && msg.get_self_mute(), + self_deaf: msg.has_self_deaf() && msg.get_self_deaf(), + mute: msg.has_mute() && msg.get_mute(), + deaf: msg.has_deaf() && msg.get_deaf(), + } + } + + pub fn parse_user_state(&mut self, mut msg: msgs::UserState) { + if msg.has_channel_id() { + self.channel = msg.get_channel_id(); + } + if msg.has_comment() { + self.comment = Some(msg.take_comment()); + } + if msg.has_hash() { + self.hash = Some(msg.take_hash()); + } + if msg.has_name() { + self.name = msg.take_name(); + } + if msg.has_priority_speaker() { + self.priority_speaker = msg.get_priority_speaker(); + } + if msg.has_recording() { + self.recording = msg.get_recording(); + } + if msg.has_suppress() { + self.suppress = msg.get_suppress(); + } + if msg.has_self_mute() { + self.self_mute = msg.get_self_mute(); + } + if msg.has_self_deaf() { + self.self_deaf = msg.get_self_deaf(); + } + if msg.has_mute() { + self.mute = msg.get_mute(); + } + if msg.has_deaf() { + self.deaf = msg.get_deaf(); + } + } + + pub fn apply_user_diff(&mut self, diff: &mumlib::state::UserDiff) { + debug!("applying user diff\n{:#?}", diff); + if let Some(comment) = diff.comment.clone() { + self.comment = Some(comment); + } + if let Some(hash) = diff.hash.clone() { + self.hash = Some(hash); + } + if let Some(name) = diff.name.clone() { + self.name = name; + } + if let Some(priority_speaker) = diff.priority_speaker { + self.priority_speaker = priority_speaker; + } + if let Some(recording) = diff.recording { + self.recording = recording; + } + if let Some(suppress) = diff.suppress { + self.suppress = suppress; + } + if let Some(self_mute) = diff.self_mute { + self.self_mute = self_mute; + } + if let Some(self_deaf) = diff.self_deaf { + self.self_deaf = self_deaf; + } + if let Some(mute) = diff.mute { + self.mute = mute; + } + if let Some(deaf) = diff.deaf { + self.deaf = deaf; + } + + if let Some(channel_id) = diff.channel_id { + self.channel = channel_id; + } + } + + + pub fn name(&self) -> &str { + &self.name + } + + pub fn channel(&self) -> u32 { + self.channel + } +} + +impl From<&User> for mumlib::state::User { + fn from(user: &User) -> Self { + mumlib::state::User { + comment: user.comment.clone(), + hash: user.hash.clone(), + name: user.name.clone(), + priority_speaker: user.priority_speaker, + recording: user.recording, + suppress: user.suppress, + self_mute: user.self_mute, + self_deaf: user.self_deaf, + mute: user.mute, + deaf: user.deaf, + } + } +} |
