aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEskil Queseth <eskilq@kth.se>2020-10-17 21:28:40 +0200
committerEskil Queseth <eskilq@kth.se>2020-10-17 21:28:40 +0200
commitccc5f76133460d055a5d90ae9cd0a7bc2a83551d (patch)
tree43d27a68fe15ef5f28bd08161bd03d1a39a00186
parent7675171e2c307c91a81b0daee915c2a114ae4612 (diff)
parentf1df606dfeafbfe93066d23f378d7fdbd1b4e9e1 (diff)
downloadmum-ccc5f76133460d055a5d90ae9cd0a7bc2a83551d.tar.gz
Merge remote-tracking branch 'origin/pretty-print' into main
-rw-r--r--mumctl/src/main.rs143
-rw-r--r--mumd/src/command.rs5
-rw-r--r--mumd/src/main.rs41
-rw-r--r--mumd/src/state.rs425
-rw-r--r--mumlib/src/command.rs10
-rw-r--r--mumlib/src/error.rs6
-rw-r--r--mumlib/src/lib.rs2
-rw-r--r--mumlib/src/state.rs290
8 files changed, 639 insertions, 283 deletions
diff --git a/mumctl/src/main.rs b/mumctl/src/main.rs
index 82fcab6..2473195 100644
--- a/mumctl/src/main.rs
+++ b/mumctl/src/main.rs
@@ -3,7 +3,10 @@ use colored::Colorize;
use ipc_channel::ipc::{self, IpcSender};
use mumlib::command::{Command, CommandResponse};
use mumlib::setup_logger;
-use std::{fs, io};
+use mumlib::state::Channel;
+use std::{fs, io, iter};
+
+const INDENTATION: &str = " ";
macro_rules! err_print {
($func:expr) => {
@@ -18,26 +21,28 @@ fn main() {
let mut app = App::new("mumctl")
.setting(AppSettings::ArgRequiredElseHelp)
- .subcommand(SubCommand::with_name("server")
- .setting(AppSettings::ArgRequiredElseHelp)
- .subcommand(SubCommand::with_name("connect")
- .setting(AppSettings::ArgRequiredElseHelp)
- .arg(Arg::with_name("host")
- .required(true)
- .index(1))
- .arg(Arg::with_name("username")
- .required(true)
- .index(2)))
- .subcommand(SubCommand::with_name("disconnect")))
- .subcommand(SubCommand::with_name("channel")
- .setting(AppSettings::ArgRequiredElseHelp)
- .subcommand(SubCommand::with_name("list")
- .arg(Arg::with_name("short")
- .short("s")
- .long("short")))
- .subcommand(SubCommand::with_name("connect")
- .arg(Arg::with_name("channel")
- .required(true))))
+ .subcommand(
+ SubCommand::with_name("server")
+ .setting(AppSettings::ArgRequiredElseHelp)
+ .subcommand(
+ SubCommand::with_name("connect")
+ .setting(AppSettings::ArgRequiredElseHelp)
+ .arg(Arg::with_name("host").required(true).index(1))
+ .arg(Arg::with_name("username").required(true).index(2)),
+ )
+ .subcommand(SubCommand::with_name("disconnect")),
+ )
+ .subcommand(
+ SubCommand::with_name("channel")
+ .setting(AppSettings::ArgRequiredElseHelp)
+ .subcommand(
+ SubCommand::with_name("list")
+ .arg(Arg::with_name("short").short("s").long("short")),
+ )
+ .subcommand(
+ SubCommand::with_name("connect").arg(Arg::with_name("channel").required(true)),
+ ),
+ )
.subcommand(SubCommand::with_name("status"))
.subcommand(SubCommand::with_name("config")
.arg(Arg::with_name("name")
@@ -53,7 +58,7 @@ fn main() {
.long("fish")));
let matches = app.clone().get_matches();
-
+
if let Some(matches) = matches.subcommand_matches("server") {
if let Some(matches) = matches.subcommand_matches("connect") {
let host = matches.value_of("host").unwrap();
@@ -70,9 +75,12 @@ fn main() {
} else if let Some(matches) = matches.subcommand_matches("channel") {
if let Some(_matches) = matches.subcommand_matches("list") {
match send_command(Command::ChannelList) {
- Ok(res) => {
- println!("{:#?}", res.unwrap());
- }
+ Ok(res) => match res {
+ Some(CommandResponse::ChannelList { channels }) => {
+ print_channel(&channels, 0);
+ }
+ _ => unreachable!(),
+ },
Err(e) => println!("{} {}", "error:".red(), e),
}
} else if let Some(matches) = matches.subcommand_matches("connect") {
@@ -82,13 +90,36 @@ fn main() {
}
} else if let Some(_matches) = matches.subcommand_matches("status") {
match send_command(Command::Status) {
- Ok(res) => {
- println!("{:#?}", res.unwrap());
- }
+ Ok(res) => match res {
+ Some(CommandResponse::Status { server_state }) => {
+ println!(
+ "Connected to {} as {}",
+ server_state.host, server_state.username
+ );
+ let own_channel = server_state
+ .channels
+ .iter()
+ .find(|e| e.users.iter().any(|e| e.name == server_state.username))
+ .unwrap();
+ println!(
+ "Currently in {} with {} other client{}:",
+ own_channel.name,
+ own_channel.users.len() - 1,
+ if own_channel.users.len() == 2 {
+ ""
+ } else {
+ "s"
+ }
+ );
+ println!("{}{}", INDENTATION, own_channel.name);
+ for user in &own_channel.users {
+ println!("{}{}{}", INDENTATION, INDENTATION, user);
+ }
+ }
+ _ => unreachable!(),
+ },
Err(e) => println!("{} {}", "error:".red(), e),
}
- let res = send_command(Command::Status).unwrap().unwrap();
- println!("{:#?}", res);
} else if let Some(matches) = matches.subcommand_matches("config") {
let name = matches.value_of("name").unwrap();
let value = matches.value_of("value").unwrap();
@@ -103,25 +134,22 @@ fn main() {
}
}
} else if let Some(matches) = matches.subcommand_matches("completions") {
- app.gen_completions_to("mumctl",
- match matches.value_of("shell").unwrap_or("zsh") {
- "bash" => {
- Shell::Bash
- },
- "fish" => {
- Shell::Fish
- },
- _ => {
- Shell::Zsh
- },
- },
- &mut io::stdout());
- return;
+ app.gen_completions_to(
+ "mumctl",
+ match matches.value_of("shell").unwrap_or("zsh") {
+ "bash" => Shell::Bash,
+ "fish" => Shell::Fish,
+ _ => Shell::Zsh,
+ },
+ &mut io::stdout(),
+ );
+ return;
};
}
fn send_command(command: Command) -> mumlib::error::Result<Option<CommandResponse>> {
- let (tx_client, rx_client) = ipc::channel::<mumlib::error::Result<Option<CommandResponse>>>().unwrap();
+ let (tx_client, rx_client) =
+ ipc::channel::<mumlib::error::Result<Option<CommandResponse>>>().unwrap();
let server_name = fs::read_to_string("/var/tmp/mumd-oneshot").unwrap(); //TODO don't panic
@@ -131,3 +159,28 @@ fn send_command(command: Command) -> mumlib::error::Result<Option<CommandRespons
rx_client.recv().unwrap()
}
+
+fn print_channel(channel: &Channel, depth: usize) {
+ println!(
+ "{}{}{}",
+ iter::repeat(INDENTATION).take(depth).collect::<String>(),
+ channel.name.bold(),
+ if channel.max_users != 0 {
+ format!(" {}/{}", channel.users.len(), channel.max_users)
+ } else {
+ "".to_string()
+ }
+ );
+ for user in &channel.users {
+ println!(
+ "{}-{}",
+ iter::repeat(INDENTATION)
+ .take(depth + 1)
+ .collect::<String>(),
+ user
+ );
+ }
+ for child in &channel.children {
+ print_channel(child, depth + 1);
+ }
+}
diff --git a/mumd/src/command.rs b/mumd/src/command.rs
index 57eaaa3..a035a26 100644
--- a/mumd/src/command.rs
+++ b/mumd/src/command.rs
@@ -8,7 +8,10 @@ use tokio::sync::mpsc;
pub async fn handle(
state: Arc<Mutex<State>>,
- mut command_receiver: mpsc::UnboundedReceiver<(Command, IpcSender<mumlib::error::Result<Option<CommandResponse>>>)>,
+ mut command_receiver: mpsc::UnboundedReceiver<(
+ Command,
+ IpcSender<mumlib::error::Result<Option<CommandResponse>>>,
+ )>,
) {
debug!("Begin listening for commands");
while let Some(command) = command_receiver.recv().await {
diff --git a/mumd/src/main.rs b/mumd/src/main.rs
index 3a0d7ec..5ae077a 100644
--- a/mumd/src/main.rs
+++ b/mumd/src/main.rs
@@ -7,7 +7,7 @@ use crate::network::ConnectionInfo;
use crate::state::State;
use futures::join;
-use ipc_channel::ipc::{IpcSender, IpcOneShotServer};
+use ipc_channel::ipc::{IpcOneShotServer, IpcSender};
use log::*;
use mumble_protocol::control::ControlPacket;
use mumble_protocol::crypt::ClientCryptState;
@@ -27,14 +27,14 @@ async fn main() {
// For simplicity we don't deal with re-syncing, real applications would have to.
let (crypt_state_sender, crypt_state_receiver) = mpsc::channel::<ClientCryptState>(1); // crypt state should always be consumed before sending a new one
let (packet_sender, packet_receiver) = mpsc::unbounded_channel::<ControlPacket<Serverbound>>();
- let (command_sender, command_receiver) = mpsc::unbounded_channel::<(Command, IpcSender<mumlib::error::Result<Option<CommandResponse>>>)>();
+ let (command_sender, command_receiver) = mpsc::unbounded_channel::<(
+ Command,
+ IpcSender<mumlib::error::Result<Option<CommandResponse>>>,
+ )>();
let (connection_info_sender, connection_info_receiver) =
watch::channel::<Option<ConnectionInfo>>(None);
- let state = State::new(
- packet_sender,
- connection_info_sender,
- );
+ let state = State::new(packet_sender, connection_info_sender);
let state = Arc::new(Mutex::new(state));
let (_, _, _, e) = join!(
@@ -49,11 +49,9 @@ async fn main() {
connection_info_receiver.clone(),
crypt_state_receiver,
),
- command::handle(
- state,
- command_receiver,
- ),
- spawn_blocking(move || { // IpcSender is blocking
+ command::handle(state, command_receiver,),
+ spawn_blocking(move || {
+ // IpcSender is blocking
receive_oneshot_commands(command_sender);
}),
);
@@ -61,16 +59,31 @@ async fn main() {
}
fn receive_oneshot_commands(
- command_sender: mpsc::UnboundedSender<(Command, IpcSender<mumlib::error::Result<Option<CommandResponse>>>)>,
+ command_sender: mpsc::UnboundedSender<(
+ Command,
+ IpcSender<mumlib::error::Result<Option<CommandResponse>>>,
+ )>,
) {
loop {
// create listener
- let (server, server_name): (IpcOneShotServer<(Command, IpcSender<mumlib::error::Result<Option<CommandResponse>>>)>, String) = IpcOneShotServer::new().unwrap();
+ let (server, server_name): (
+ IpcOneShotServer<(
+ Command,
+ IpcSender<mumlib::error::Result<Option<CommandResponse>>>,
+ )>,
+ String,
+ ) = IpcOneShotServer::new().unwrap();
fs::write("/var/tmp/mumd-oneshot", &server_name).unwrap();
debug!("Listening to {}", server_name);
// receive command and response channel
- let (_, conn): (_, (Command, IpcSender<mumlib::error::Result<Option<CommandResponse>>>)) = server.accept().unwrap();
+ let (_, conn): (
+ _,
+ (
+ Command,
+ IpcSender<mumlib::error::Result<Option<CommandResponse>>>,
+ ),
+ ) = server.accept().unwrap();
debug!("Sending command {:?} to command handler", conn.0);
command_sender.send(conn).unwrap();
}
diff --git a/mumd/src/state.rs b/mumd/src/state.rs
index fd1c831..55fd8ae 100644
--- a/mumd/src/state.rs
+++ b/mumd/src/state.rs
@@ -6,10 +6,12 @@ use mumble_protocol::control::msgs;
use mumble_protocol::control::ControlPacket;
use mumble_protocol::voice::Serverbound;
use mumlib::command::{Command, CommandResponse};
-use mumlib::state::Server;
+use mumlib::error::{ChannelIdentifierError, Error};
+use serde::{Deserialize, Serialize};
+use std::collections::hash_map::Entry;
+use std::collections::HashMap;
use std::net::ToSocketAddrs;
use tokio::sync::{mpsc, watch};
-use mumlib::error::{ChannelIdentifierError, Error};
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum StatePhase {
@@ -26,9 +28,6 @@ pub struct State {
connection_info_sender: watch::Sender<Option<ConnectionInfo>>,
phase_watcher: (watch::Sender<StatePhase>, watch::Receiver<StatePhase>),
-
- username: Option<String>,
- session_id: Option<u32>,
}
impl State {
@@ -42,8 +41,6 @@ impl State {
packet_sender,
connection_info_sender,
phase_watcher: watch::channel(StatePhase::Disconnected),
- username: None,
- session_id: None,
}
}
@@ -83,7 +80,7 @@ impl State {
};
let mut msg = msgs::UserState::new();
- msg.set_session(self.session_id.unwrap());
+ msg.set_session(self.server.as_ref().unwrap().session_id.unwrap());
msg.set_channel_id(id);
self.packet_sender.send(msg.into()).unwrap();
(false, Ok(None))
@@ -92,8 +89,13 @@ impl State {
if !matches!(*self.phase_receiver().borrow(), StatePhase::Connected) {
return (false, Err(Error::DisconnectedError));
}
- (false, Ok(Some(CommandResponse::ChannelList {
- channels: self.server().unwrap().channels().clone(),
+ (
+ false,
+ Ok(Some(CommandResponse::ChannelList {
+ channels: into_channel(
+ self.server.as_ref().unwrap().channels(),
+ self.server.as_ref().unwrap().users(),
+ ),
})),
)
}
@@ -106,14 +108,19 @@ impl State {
if !matches!(*self.phase_receiver().borrow(), StatePhase::Disconnected) {
return (false, Err(Error::AlreadyConnectedError));
}
- self.server = Some(Server::new());
- self.username = Some(username);
+ let mut server = Server::new();
+ server.username = Some(username);
+ server.host = Some(format!("{}:{}", host, port));
+ self.server = Some(server);
self.phase_watcher
.0
.broadcast(StatePhase::Connecting)
.unwrap();
- let socket_addr = match (host.as_ref(), port).to_socket_addrs().map(|mut e| e.next()) {
+ let socket_addr = match (host.as_ref(), port)
+ .to_socket_addrs()
+ .map(|mut e| e.next())
+ {
Ok(Some(v)) => v,
_ => {
warn!("Error parsing server addr");
@@ -136,8 +143,7 @@ impl State {
(
false,
Ok(Some(CommandResponse::Status {
- username: self.username.clone(),
- server_state: self.server.clone().unwrap(), //guaranteed not to panic because if we are connected, server is guaranteed to be Some
+ server_state: self.server.as_ref().unwrap().into(), //guaranteed not to panic because if we are connected, server is guaranteed to be Some
})),
)
}
@@ -146,8 +152,6 @@ impl State {
return (false, Err(Error::DisconnectedError));
}
- self.session_id = None;
- self.username = None;
self.server = None;
self.audio.clear_clients();
@@ -171,11 +175,11 @@ impl State {
}
if !msg.has_name() {
warn!("Missing name in initial user state");
- } else if msg.get_name() == self.username.as_ref().unwrap() {
- match self.session_id {
+ } else if msg.get_name() == self.server.as_ref().unwrap().username.as_ref().unwrap() {
+ match self.server.as_ref().unwrap().session_id {
None => {
debug!("Found our session id: {}", msg.get_session());
- self.session_id = Some(msg.get_session());
+ self.server_mut().unwrap().session_id = Some(msg.get_session());
}
Some(session) => {
if session != msg.get_session() {
@@ -218,7 +222,384 @@ impl State {
pub fn server_mut(&mut self) -> Option<&mut Server> {
self.server.as_mut()
}
- pub fn username(&self) -> Option<&String> {
- self.username.as_ref()
+ pub fn username(&self) -> Option<&str> {
+ self.server.as_ref().map(|e| e.username()).flatten()
+ }
+}
+
+#[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 users(&self) -> &HashMap<u32, User> {
+ &self.users
+ }
+
+ pub fn username(&self) -> Option<&str> {
+ self.username.as_ref().map(|e| e.as_str())
+ }
+}
+
+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(),
+ }
+ }
+}
+
+#[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(),
+ }
+ }
+}
+
+#[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,
+ }
}
}
diff --git a/mumlib/src/command.rs b/mumlib/src/command.rs
index b27d3ca..b4ab07a 100644
--- a/mumlib/src/command.rs
+++ b/mumlib/src/command.rs
@@ -1,7 +1,6 @@
use crate::state::{Channel, Server};
use serde::{Deserialize, Serialize};
-use std::collections::HashMap;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum Command {
@@ -22,11 +21,6 @@ pub enum Command {
#[derive(Debug, Deserialize, Serialize)]
pub enum CommandResponse {
- ChannelList {
- channels: HashMap<u32, Channel>,
- },
- Status {
- username: Option<String>,
- server_state: Server,
- },
+ ChannelList { channels: Channel },
+ Status { server_state: Server },
}
diff --git a/mumlib/src/error.rs b/mumlib/src/error.rs
index 6c66c1f..a4c6dcb 100644
--- a/mumlib/src/error.rs
+++ b/mumlib/src/error.rs
@@ -1,6 +1,6 @@
-use serde::{Serialize, Deserialize};
-use std::fmt::Display;
use serde::export::Formatter;
+use serde::{Deserialize, Serialize};
+use std::fmt::Display;
pub type Result<T> = std::result::Result<T, Error>;
@@ -37,4 +37,4 @@ impl Display for ChannelIdentifierError {
ChannelIdentifierError::Ambiguous => write!(f, "Ambiguous channel identifier"),
}
}
-} \ No newline at end of file
+}
diff --git a/mumlib/src/lib.rs b/mumlib/src/lib.rs
index b77653c..d989a7e 100644
--- a/mumlib/src/lib.rs
+++ b/mumlib/src/lib.rs
@@ -1,6 +1,6 @@
pub mod command;
-pub mod state;
pub mod error;
+pub mod state;
use colored::*;
use log::*;
diff --git a/mumlib/src/state.rs b/mumlib/src/state.rs
index 51fb492..b09726e 100644
--- a/mumlib/src/state.rs
+++ b/mumlib/src/state.rs
@@ -1,224 +1,136 @@
-use log::*;
-use mumble_protocol::control::msgs;
+use serde::export::Formatter;
use serde::{Deserialize, Serialize};
-use std::collections::hash_map::Entry;
-use std::collections::HashMap;
+use std::fmt::Display;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Server {
- channels: HashMap<u32, Channel>,
- users: HashMap<u32, User>,
+ pub channels: Channel,
pub welcome_text: Option<String>,
-}
-
-impl Server {
- pub fn new() -> Self {
- Self {
- channels: HashMap::new(),
- users: HashMap::new(),
- welcome_text: 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 users(&self) -> &HashMap<u32, User> {
- &self.users
- }
+ pub username: String,
+ pub host: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Channel {
- description: Option<String>,
- links: Vec<u32>,
- max_users: u32,
- name: String,
- parent: Option<u32>,
- position: i32,
+ pub description: Option<String>,
+ pub links: Vec<Vec<usize>>, //to represent several walks through the tree to find channels its linked to
+ pub max_users: u32,
+ pub name: String,
+ pub children: Vec<Channel>,
+ pub users: Vec<User>,
}
impl Channel {
- pub fn new(mut msg: msgs::ChannelState) -> Self {
- Self {
- description: if msg.has_description() {
- Some(msg.take_description())
+ pub fn iter(&self) -> Iter<'_> {
+ Iter {
+ me: Some(&self),
+ channel: if self.children.len() > 0 {
+ Some(0)
} 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(),
+ channels: self.children.iter().map(|e| e.iter()).collect(),
}
}
- 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 users_iter(&self) -> UsersIter<'_> {
+ UsersIter {
+ channels: self.children.iter().map(|e| e.users_iter()).collect(),
+ channel: if self.children.len() > 0 {
+ Some(0)
+ } else {
+ None
+ },
+ user: if self.users.len() > 0 { Some(0) } else { None },
+ users: &self.users,
}
}
+}
- pub fn name(&self) -> &str {
- &self.name
- }
+pub struct Iter<'a> {
+ me: Option<&'a Channel>,
+ channel: Option<usize>,
+ channels: Vec<Iter<'a>>,
+}
- 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(),
+impl<'a> Iterator for Iter<'a> {
+ type Item = &'a Channel;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.me.is_some() {
+ self.me.take()
+ } else if let Some(mut c) = self.channel {
+ let mut n = self.channels[c].next();
+ while n.is_none() {
+ c += 1;
+ if c >= self.channels.len() {
+ self.channel = None;
+ return None;
+ }
+ n = self.channels[c].next();
+ }
+ self.channel = Some(c);
+ n
+ } else {
+ None
}
}
}
-#[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
+pub struct UsersIter<'a> {
+ channel: Option<usize>,
+ channels: Vec<UsersIter<'a>>,
+ user: Option<usize>,
+ users: &'a [User],
}
-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(),
- }
- }
+impl<'a> Iterator for UsersIter<'a> {
+ type Item = &'a User;
- 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();
+ fn next(&mut self) -> Option<Self::Item> {
+ if let Some(u) = self.user {
+ let ret = Some(&self.users[u]);
+ if u + 1 < self.users.len() {
+ self.user = Some(u + 1);
+ } else {
+ self.user = None;
+ }
+ ret
+ } else if let Some(mut c) = self.channel {
+ let mut n = self.channels[c].next();
+ while n.is_none() {
+ c += 1;
+ if c >= self.channels.len() {
+ self.channel = None;
+ return None;
+ }
+ n = self.channels[c].next();
+ }
+ self.channel = Some(c);
+ n
+ } else {
+ None
}
}
+}
- pub fn name(&self) -> &str {
- &self.name
- }
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct User {
+ pub comment: Option<String>,
+ pub hash: Option<String>,
+ pub name: String,
+ pub priority_speaker: bool,
+ pub recording: bool,
+
+ pub suppress: bool, // by me
+ pub self_mute: bool, // by self
+ pub self_deaf: bool, // by self
+ pub mute: bool, // by admin
+ pub deaf: bool, // by admin
+}
- pub fn channel(&self) -> u32 {
- self.channel
+impl Display for User {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.name)
}
}