aboutsummaryrefslogtreecommitdiffstats
path: root/mumlib
diff options
context:
space:
mode:
Diffstat (limited to 'mumlib')
-rw-r--r--mumlib/src/command.rs62
-rw-r--r--mumlib/src/config.rs56
-rw-r--r--mumlib/src/lib.rs10
-rw-r--r--mumlib/src/state.rs39
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>>,