use crate::state::user::User; use mumble_protocol::control::msgs; use serde::{Deserialize, Serialize}; use std::collections::HashMap; #[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct Channel { description: Option, links: Vec, max_users: u32, name: String, parent: Option, 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) -> 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>, users: Vec<&'a User>, } impl<'a> ProtoTree<'a> { fn walk_and_add( &mut self, channel: &'a Channel, users: &HashMap>, 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).cloned().unwrap_or_default(); } 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::>(); 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, users: &HashMap, ) -> 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_with(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.is_empty() { 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).cloned().unwrap_or_default(), }; 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::new( channel.name.clone(), channel.description.clone(), channel.max_users, ) } }