diff options
Diffstat (limited to 'mumlib')
| -rw-r--r-- | mumlib/src/command.rs | 62 | ||||
| -rw-r--r-- | mumlib/src/config.rs | 56 | ||||
| -rw-r--r-- | mumlib/src/lib.rs | 10 | ||||
| -rw-r--r-- | mumlib/src/state.rs | 39 |
4 files changed, 159 insertions, 8 deletions
diff --git a/mumlib/src/command.rs b/mumlib/src/command.rs index 8cb7cb9..9d98a38 100644 --- a/mumlib/src/command.rs +++ b/mumlib/src/command.rs @@ -1,3 +1,6 @@ +//! [Command]s can be sent from a controller to mumd which might respond with a +//! [CommandResponse]. + use crate::state::{Channel, Server}; use chrono::NaiveDateTime; @@ -7,7 +10,9 @@ use std::fmt; /// Something that happened in our channel at a point in time. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MumbleEvent { + /// When the event occured. pub timestamp: NaiveDateTime, + /// What occured. pub kind: MumbleEventKind } @@ -20,11 +25,17 @@ impl fmt::Display for MumbleEvent { /// The different kinds of events that can happen. #[derive(Debug, Clone, Serialize, Deserialize)] pub enum MumbleEventKind { + /// A user connected to the server and joined our channel. Contains `(user, channel)`. UserConnected(String, String), + /// A user disconnected from the server while in our channel. Contains `(user, channel)`. UserDisconnected(String, String), + /// A user {un,}{muted,deafened}. Contains a rendered message with what changed and the user. UserMuteStateChanged(String), // This logic is kinda weird so we only store the rendered message. + /// A text message was received. Contains who sent the message. TextMessageReceived(String), + /// A user switched to our channel from some other channel. Contains `(user, previous-channel)`. UserJoinedChannel(String, String), + /// A user switched from our channel to some other channel. Contains `(user, new-channel)`. UserLeftChannel(String, String), } @@ -56,73 +67,124 @@ impl fmt::Display for MumbleEventKind { } } +/// Sent by a controller to mumd who might respond with a [CommandResponse]. Not +/// all commands receive a response. #[derive(Clone, Debug, Deserialize, Serialize)] pub enum Command { + /// No response. ChannelJoin { channel_identifier: String, }, + + /// Response: [CommandResponse::ChannelList]. ChannelList, + + /// Force reloading of config file from disk. No response. ConfigReload, + /// Response: [CommandResponse::DeafenStatus]. Toggles if None. DeafenSelf(Option<bool>), Events { block: bool }, + /// Set the outgoing audio volume (i.e. from you to the server). No response. InputVolumeSet(f32), + + /// Response: [CommandResponse::MuteStatus]. Toggles mute state if None. MuteOther(String, Option<bool>), + + /// Response: [CommandResponse::MuteStatus]. Toggles mute state if None. MuteSelf(Option<bool>), + + /// Set the master incoming audio volume (i.e. from the server to you). + /// No response. OutputVolumeSet(f32), + + /// Request a list of past messages. Blocks while waiting for more messages + /// if block is true. Response: multiple [CommandResponse::PastMessage]. PastMessages { block: bool, }, + + /// Response: [CommandResponse::Pong]. Used to test existance of a + /// mumd-instance. Ping, + + /// Send a message to some [MessageTarget]. SendMessage { + /// The message to send. message: String, + /// The target(s) to send the message to. targets: MessageTarget, }, + + /// Connect to the specified server. Response: [CommandResponse::ServerConnect]. ServerConnect { + /// The URL or IP-adress to connect to. host: String, + /// The port to connect to. port: u16, + /// The username to connect with. username: String, + /// The server password, if applicable. Not sent if None. password: Option<String>, + /// Whether to accept an invalid server certificate or not. accept_invalid_cert: bool, }, + + /// Disconnect from the currently connected server. No response. ServerDisconnect, + + /// Send a server status request via UDP (e.g. not requiring a TCP connection). + /// Response: [CommandResponse::ServerStatus]. ServerStatus { host: String, port: u16, }, + + /// Request the status of the current server. Response: [CommandResponse::Status]. Status, + + /// The the volume of the specified user. No response. UserVolumeSet(String, f32), } +/// A response to a sent [Command]. #[derive(Debug, Deserialize, Serialize)] pub enum CommandResponse { ChannelList { channels: Channel, }, + DeafenStatus { is_deafened: bool, }, + Event { event: MumbleEvent, }, + MuteStatus { is_muted: bool, }, + PastMessage { message: (NaiveDateTime, String, String), }, + Pong, + ServerConnect { welcome_message: Option<String>, server_state: Server, }, + ServerStatus { version: u32, users: u32, max_users: u32, bandwidth: u32, }, + Status { server_state: Server, }, diff --git a/mumlib/src/config.rs b/mumlib/src/config.rs index 3edef37..932e013 100644 --- a/mumlib/src/config.rs +++ b/mumlib/src/config.rs @@ -1,3 +1,5 @@ +//! Representations of the mumdrc configuration file. + use crate::error::ConfigError; use crate::DEFAULT_PORT; @@ -23,9 +25,13 @@ struct TOMLConfig { servers: Option<Array>, } +/// Our representation of the mumdrc config file. +// Deserialized via [TOMLConfig]. #[derive(Clone, Debug, Default)] pub struct Config { + /// General audio configuration. pub audio: AudioConfig, + /// Saved servers. pub servers: Vec<ServerConfig>, /// Whether we allow connecting to servers with invalid server certificates. /// @@ -34,6 +40,15 @@ pub struct Config { } impl Config { + /// Writes this config to the specified path. + /// + /// Pass create = true if you want the file to be created if it doesn't already exist. + /// + /// # Errors + /// + /// - [ConfigError::WontCreateFile] if the file doesn't exist and create = false was passed. + /// - Any [ConfigError::TOMLErrorSer] encountered when serializing the config. + /// - Any [ConfigError::IOError] encountered when writing the file. pub fn write(&self, path: &Path, create: bool) -> Result<(), ConfigError> { // Possible race here. It's fine since it shows when: // 1) the file doesn't exist when checked and is then created @@ -55,41 +70,60 @@ impl Config { } } +/// Overwrite a specific sound effect with a file that should be played instead. #[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct SoundEffect { + /// During which event the effect should be played. pub event: String, + /// The file that should be played. pub file: String, } +/// General audio configuration. #[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct AudioConfig { + /// The microphone input sensitivity. pub input_volume: Option<f32>, + /// The output main gain. pub output_volume: Option<f32>, + /// Overriden sound effects. pub sound_effects: Option<Vec<SoundEffect>>, } +/// A saved server. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ServerConfig { + /// The alias of the server. pub name: String, + /// The host (URL or IP-adress) of the server. pub host: String, + /// The port, if non-default. pub port: Option<u16>, + /// The username to connect with. Prompted on connection if omitted. pub username: Option<String>, + /// The password to connect with. Nothing is sent to the server if omitted. pub password: Option<String>, + /// Whether to accept invalid server certifications for this server. pub accept_invalid_cert: Option<bool>, } impl ServerConfig { + /// Creates a [SocketAddr] for this server. + /// + /// Returns `None` if no resolution could be made. See + /// [std::net::ToSocketAddrs] for more information. pub fn to_socket_addr(&self) -> Option<SocketAddr> { - match (self.host.as_str(), self.port.unwrap_or(DEFAULT_PORT)) - .to_socket_addrs() - .map(|mut e| e.next()) - { - Ok(Some(addr)) => Some(addr), - _ => None, - } + Some((self.host.as_str(), self.port.unwrap_or(DEFAULT_PORT)) + .to_socket_addrs() + .ok()? + .next()?) } } +/// Finds the default path of the configuration file. +/// +/// The user config dir is looked for first (cross-platform friendly) and +/// `/etc/mumdrc` is used as a fallback. pub fn default_cfg_path() -> PathBuf { match dirs::config_dir() { Some(mut p) => { @@ -143,6 +177,14 @@ impl From<Config> for TOMLConfig { } } +/// Reads the config at the specified path. +/// +/// If the file isn't found, returns a default config. +/// +/// # Errors +/// +/// - Any [ConfigError::TOMLErrorDe] encountered when deserializing the config. +/// - Any [ConfigError::IOError] encountered when reading the file. pub fn read_cfg(path: &Path) -> Result<Config, ConfigError> { match fs::read_to_string(path) { Ok(s) => { diff --git a/mumlib/src/lib.rs b/mumlib/src/lib.rs index 9b7d686..679db8d 100644 --- a/mumlib/src/lib.rs +++ b/mumlib/src/lib.rs @@ -1,3 +1,7 @@ +//! Shared items for crates that want to communicate with mumd and/or mumctl. + +// #![warn(missing_docs)] + pub mod command; pub mod config; pub mod error; @@ -8,9 +12,15 @@ pub use error::Error; use colored::*; use log::*; +/// The default file path to use for the socket. pub const SOCKET_PATH: &str = "/tmp/mumd"; + +/// The default mumble port. pub const DEFAULT_PORT: u16 = 64738; +/// Setup a minimal fern logger. +/// +/// Format: `LEVEL [yyyy-mm-dd][HH:MM:SS] FILE:LINE MESSAGE` pub fn setup_logger<T: Into<fern::Output>>(target: T, color: bool) { fern::Dispatch::new() .format(move |out, message, record| { diff --git a/mumlib/src/state.rs b/mumlib/src/state.rs index 6fad332..72c01a6 100644 --- a/mumlib/src/state.rs +++ b/mumlib/src/state.rs @@ -1,25 +1,58 @@ use serde::{Deserialize, Serialize}; use std::fmt; +/// The state of the currently connected Mumble server. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Server { + /// State of the currently connected channel. pub channels: Channel, + /// The welcome text we received when we connected. pub welcome_text: Option<String>, + /// Our username. pub username: String, + /// The host (ip:port) of the server. pub host: String, } +/// A representation of a channel in a Mumble server. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Channel { + /// The description of the channel, if set. pub description: Option<String>, - pub links: Vec<Vec<usize>>, //to represent several walks through the tree to find channels its linked to + /// The maximum number of allowed users in this channel. pub max_users: u32, + /// The name of this channel. pub name: String, + /// Any children this channel has. pub children: Vec<Channel>, + /// This channel's connected users. pub users: Vec<User>, + + links: Vec<Vec<usize>>, //to represent several walks through the tree to find channels its linked to } impl Channel { + /// Create a new Channel representation. + pub fn new( + name: String, + description: Option<String>, + max_users: u32, + ) -> Self { + Self { + description, + max_users, + name, + children: Vec::new(), + users: Vec::new(), + + links: Vec::new(), + } + } + + /// Create an iterator over this channel and its children in a [pre-order + /// traversal](https://en.wikipedia.org/wiki/Tree_traversal#Pre-order,_NLR) + /// which ensures that parent channels are returned before any of its + /// children. pub fn iter(&self) -> Iter<'_> { Iter { me: Some(&self), @@ -32,6 +65,8 @@ impl Channel { } } + /// Create an iterator over this channel and its childrens connected users + /// in a pre-order traversal. pub fn users_iter(&self) -> UsersIter<'_> { UsersIter { channels: self.children.iter().map(|e| e.users_iter()).collect(), @@ -46,6 +81,7 @@ impl Channel { } } +/// An iterator over channels. Created by [Channel::iter]. pub struct Iter<'a> { me: Option<&'a Channel>, channel: Option<usize>, @@ -76,6 +112,7 @@ impl<'a> Iterator for Iter<'a> { } } +/// An iterator over users. Created by [Channel::users_iter]. pub struct UsersIter<'a> { channel: Option<usize>, channels: Vec<UsersIter<'a>>, |
