aboutsummaryrefslogtreecommitdiffstats
path: root/mumd/src/state
diff options
context:
space:
mode:
authorGustav Sörnäs <gustav@sornas.net>2020-10-21 04:01:23 +0200
committerGustav Sörnäs <gustav@sornas.net>2020-10-21 04:01:23 +0200
commit962af6bd39f77381aeca874a40fa0b2fb36f33c4 (patch)
treedb080f965c0e15b9f9f092fe466e7b88db7191eb /mumd/src/state
parent1c18f57afe3eae3d61ad44c899d57332e0f71e0c (diff)
downloadmum-962af6bd39f77381aeca874a40fa0b2fb36f33c4.tar.gz
refactor state into {channel,server,user}.rs
Diffstat (limited to 'mumd/src/state')
-rw-r--r--mumd/src/state/channel.rs184
-rw-r--r--mumd/src/state/server.rs119
-rw-r--r--mumd/src/state/user.rs105
3 files changed, 408 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(&current).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..b7cabb7
--- /dev/null
+++ b/mumd/src/state/server.rs
@@ -0,0 +1,119 @@
+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 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..bb4e101
--- /dev/null
+++ b/mumd/src/state/user.rs
@@ -0,0 +1,105 @@
+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 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,
+ }
+ }
+}