diff options
| -rw-r--r-- | Cargo.lock | 152 | ||||
| -rw-r--r-- | mumctl/Cargo.toml | 9 | ||||
| -rw-r--r-- | mumctl/src/main.rs | 883 | ||||
| -rw-r--r-- | mumlib/Cargo.toml | 4 | ||||
| -rw-r--r-- | mumlib/src/error.rs | 7 |
5 files changed, 507 insertions, 548 deletions
@@ -32,6 +32,12 @@ dependencies = [ ] [[package]] +name = "anyhow" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" + +[[package]] name = "arrayref" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -184,7 +190,6 @@ dependencies = [ "textwrap", "unicode-width", "vec_map", - "yaml-rust", ] [[package]] @@ -257,9 +262,9 @@ dependencies = [ [[package]] name = "cpal" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "840981d3f30230d9120328d64be72319dbbedabb61bcd4c370a54cdd051238ac" +checksum = "8351ddf2aaa3c583fa388029f8b3d26f3c7035a20911fdd5f2e2ed7ab57dad25" dependencies = [ "alsa", "core-foundation-sys 0.6.2", @@ -410,9 +415,9 @@ dependencies = [ [[package]] name = "dasp_window" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66bcb90ea007ba45fc48d426e28af3e8a653634f9a7174d768dcfe90fa6211f4" +checksum = "99ded7b88821d2ce4e8b842c9f1c86ac911891ab89443cc1de750cae764c5076" dependencies = [ "dasp_sample", ] @@ -708,9 +713,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.49" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc15e39392125075f60c95ba416f5381ff6c3a948ff02ab12464715adf56c821" +checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c" dependencies = [ "wasm-bindgen", ] @@ -729,9 +734,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.90" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4aede83fc3617411dc6993bc8c70919750c1c257c6ca6a502aed6e0e2394ae" +checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714" [[package]] name = "libloading" @@ -806,9 +811,9 @@ checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "mio" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2182a122f3b7f3f5329cb1972cee089ba2459a0a80a56935e6e674f096f8d839" +checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" dependencies = [ "libc", "log", @@ -819,11 +824,10 @@ dependencies = [ [[package]] name = "miow" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" dependencies = [ - "socket2", "winapi", ] @@ -845,11 +849,12 @@ dependencies = [ name = "mumctl" version = "0.3.0" dependencies = [ + "anyhow", "bincode", - "clap", "colored", "log", "mumlib", + "structopt", ] [[package]] @@ -1188,6 +1193,30 @@ dependencies = [ ] [[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] name = "proc-macro-hack" version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1201,9 +1230,9 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" dependencies = [ "unicode-xid", ] @@ -1377,9 +1406,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "security-framework" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d493c5f39e02dfb062cd8f33301f90f9b13b650e8c1b1d0fd75c19dd64bff69d" +checksum = "3670b1d2fdf6084d192bc71ead7aabe6c06aa2ea3fbd9cc3ac111fa5c2b1bd84" dependencies = [ "bitflags", "core-foundation", @@ -1390,9 +1419,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee48cdde5ed250b0d3252818f646e174ab414036edb884dde62d80a3ac6082d" +checksum = "3676258fd3cfe2c9a0ec99ce3038798d847ce3e4bb17746373eb9f0f1ac16339" dependencies = [ "core-foundation-sys 0.8.2", "libc", @@ -1400,18 +1429,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.124" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f" +checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.124" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b" +checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" dependencies = [ "proc-macro2", "quote", @@ -1437,17 +1466,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" [[package]] -name = "socket2" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" -dependencies = [ - "cfg-if", - "libc", - "winapi", -] - -[[package]] name = "stdweb" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1466,6 +1484,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" [[package]] +name = "structopt" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "strum" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1485,9 +1527,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.64" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f" +checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87" dependencies = [ "proc-macro2", "quote", @@ -1644,9 +1686,9 @@ checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] name = "walkdir" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", "winapi", @@ -1667,9 +1709,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.72" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe8f61dba8e5d645a4d8132dc7a0a66861ed5e1045d2c0ed940fab33bac0fbe" +checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1677,9 +1719,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.72" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046ceba58ff062da072c7cb4ba5b22a37f00a302483f7e2a6cdc18fedbdc1fd3" +checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae" dependencies = [ "bumpalo", "lazy_static", @@ -1692,9 +1734,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.72" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef9aa01d36cda046f797c57959ff5f3c615c9cc63997a8d545831ec7976819b" +checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1702,9 +1744,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.72" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96eb45c1b2ee33545a813a92dbb53856418bf7eb54ab34f7f7ff1448a5b3735d" +checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" dependencies = [ "proc-macro2", "quote", @@ -1715,15 +1757,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.72" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7148f4696fb4960a346eaa60bbfb42a1ac4ebba21f750f75fc1375b098d5ffa" +checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" [[package]] name = "web-sys" -version = "0.3.49" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fe19d70f5dacc03f6e46777213facae5ac3801575d56ca6cbd4c93dcd12310" +checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be" dependencies = [ "js-sys", "wasm-bindgen", @@ -1759,9 +1801,3 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "yaml-rust" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992" diff --git a/mumctl/Cargo.toml b/mumctl/Cargo.toml index 02326c8..5a12d8f 100644 --- a/mumctl/Cargo.toml +++ b/mumctl/Cargo.toml @@ -11,11 +11,12 @@ repository = "https://github.com/sornas/mum" license = "MIT" [dependencies] -mumlib = { version = "0.3", path = "../mumlib" } +mumlib = { path = "../mumlib" } -clap = { version = "2.33", features = ["yaml"] } -colored = "2.0" +anyhow = "1" +bincode = "1" +colored = "2" log = "0.4" -bincode = "1.3.2" +structopt = "0.3" #cursive = "0.15" diff --git a/mumctl/src/main.rs b/mumctl/src/main.rs index a187a3a..95b98f2 100644 --- a/mumctl/src/main.rs +++ b/mumctl/src/main.rs @@ -1,10 +1,11 @@ -use clap::{App, AppSettings, Arg, Shell, SubCommand, ArgMatches}; +use anyhow::Result; use colored::Colorize; -use log::{Record, Level, Metadata, LevelFilter, error, warn}; -use mumlib::command::{Command, CommandResponse}; -use mumlib::config::{self, ServerConfig, Config}; -use mumlib::state::Channel; -use std::{io::{self, Read, BufRead, Write}, iter, fmt::{Display, Formatter}, os::unix::net::UnixStream}; +use log::*; +use mumlib::command::{Command as MumCommand, CommandResponse}; +use mumlib::config::{self, Config, ServerConfig}; +use mumlib::state::Channel as MumChannel; +use std::{io::{self, Read, BufRead, Write}, iter, fmt::Formatter, os::unix::net::UnixStream}; +use structopt::{clap::Shell, StructOpt}; const INDENTATION: &str = " "; @@ -41,152 +42,287 @@ impl log::Log for SimpleLogger { static LOGGER: SimpleLogger = SimpleLogger; -fn main() { +#[derive(Debug, StructOpt)] +struct Mum { + #[structopt(subcommand)] + command: Command, +} + +#[derive(Debug, StructOpt)] +enum Command { + /// Connect to a server + Connect { + host: String, + username: Option<String>, + password: Option<String>, + port: Option<u16>, + }, + /// Disconnect from the currently connected server + Disconnect, + /// Handle servers + Server(Server), + /// Handle channels in the connected server + Channel(Channel), + /// Show current status + Status, + /// Change config values + Config { + key: String, + value: String, + }, + /// Reload the config file + ConfigReload, + /// Output CLI completions + Completions(Completions), + /// Change volume of either you or someone else + Volume { + user: String, + volume: Option<f32>, + }, + /// Mute someone/yourself + Mute { + user: Option<String>, + }, + /// Unmute someone/yourself + Unmute { + user: Option<String>, + }, + /// Deafen yourself + Deafen, + /// Undeafen yourself + Undeafen, +} + +#[derive(Debug, StructOpt)] +enum Server { + /// Configure a saved server + Config { + server_name: Option<String>, + key: Option<String>, + value: Option<String>, + }, + /// Rename a saved server + Rename { + old_name: String, + new_name: String + }, + /// Add a new saved server + Add { + name: String, + host: String, + port: Option<u16>, + username: Option<String>, + password: Option<String>, + }, + /// Remove a saved server + Remove { + name: String, + }, + /// List saved servers and number of people connected + List, +} + +#[derive(Debug, StructOpt)] +enum Channel { + List { + #[structopt(short = "s", long = "short")] + short: bool, + }, + Connect { + name: String, + }, +} + +#[derive(Debug, StructOpt)] +enum Completions { + Zsh, + Bash, + Fish, +} + +#[derive(Debug)] +enum CliError { + NoUsername, + ConnectionError, + NoServerFound(String), + NotSet(String), + UseServerRename, + ConfigKeyNotFound(String), + ServerAlreadyExists(String), + NoServers, +} + +impl std::error::Error for CliError {} + +impl std::fmt::Display for CliError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + CliError::NoUsername => { + write!(f, "No username specified") + } + CliError::ConnectionError => { + write!(f, "Unable to connect to mumd. Is mumd running?") + } + CliError::NoServerFound(s) => { + write!(f, "Server '{}' not found", s) + } + CliError::NotSet(s) => { + write!(f, "Key '{}' not set", s) //TODO + } + CliError::UseServerRename => { + write!(f, "Use 'server rename' instead") + } + CliError::ConfigKeyNotFound(s) => { + write!(f, "Key '{}' not found", s) + } + CliError::ServerAlreadyExists(s) => { + write!(f, "A server named '{}' already exists", s) + } + CliError::NoServers => { + write!(f, "No servers found") + } + } + } +} + +fn main() -> Result<()> { log::set_logger(&LOGGER) .map(|()| log::set_max_level(LevelFilter::Info)).unwrap(); - let mut config = match config::read_default_cfg() { - Ok(c) => c, - Err(e) => { - error!("Couldn't read config: {}", e); - return; - } - }; + let mut config = config::read_default_cfg()?; - let mut app = App::new("mumctl") - .setting(AppSettings::ArgRequiredElseHelp) - .version(env!("VERSION")) - .subcommand( - SubCommand::with_name("connect") - .about("Connect to a server") - .arg(Arg::with_name("host").required(true)) - .arg(Arg::with_name("username")) - .arg(Arg::with_name("password")) - .arg( - Arg::with_name("port") - .long("port") - .short("p") - .takes_value(true), - ), - ) - .subcommand( - SubCommand::with_name("disconnect") - .about("Disconnect from the currently connected server"), - ) - .subcommand( - SubCommand::with_name("server") - .setting(AppSettings::ArgRequiredElseHelp) - .about("Handle servers") - .subcommand( - SubCommand::with_name("config") - .about("Configure a saved server") - .arg(Arg::with_name("server_name")) - .arg(Arg::with_name("var_name")) - .arg(Arg::with_name("var_value")), - ) - .subcommand( - SubCommand::with_name("rename") - .about("Rename a saved server") - .arg(Arg::with_name("prev_name").required(true)) - .arg(Arg::with_name("next_name").required(true)), - ) - .subcommand( - SubCommand::with_name("add") - .about("Add a new saved server") - .arg(Arg::with_name("name").required(true)) - .arg(Arg::with_name("host").required(true)) - .arg( - Arg::with_name("port") - .long("port") - .takes_value(true) - .default_value("64738"), - ) - .arg( - Arg::with_name("username") - .long("username") - .takes_value(true), - ) - .arg( - Arg::with_name("password") - .long("password") - .takes_value(true), - ), - ) - .subcommand( - SubCommand::with_name("remove") - .about("Remove a saved server") - .arg(Arg::with_name("name").required(true)), - ) - .subcommand( - SubCommand::with_name("list") - .about("List saved servers and number of people connected"), + let opt = Mum::from_args(); + match opt.command { + Command::Connect { host, username, password, port } => { + let port = port.unwrap_or(mumlib::DEFAULT_PORT); + + let (host, username, password, port) = match config.servers.iter().find(|e| e.name == host) { + Some(server) => ( + &server.host, + server.username.as_ref().or(username.as_ref()).ok_or(CliError::NoUsername)?, + server.password.as_ref().or(password.as_ref()), + server.port.unwrap_or(port), ), - ) - .subcommand( - SubCommand::with_name("channel") - .about("Handle channels in the connected server") - .setting(AppSettings::ArgRequiredElseHelp) - .subcommand( - SubCommand::with_name("list") - .about("List all channels") - .arg(Arg::with_name("short").long("short").short("s")), - ) - .subcommand( - SubCommand::with_name("connect") - .about("Connect to another channel") - .arg(Arg::with_name("channel").required(true)), + None => ( + &host, + username.as_ref().ok_or(CliError::NoUsername)?, + password.as_ref(), + port, ), - ) - .subcommand(SubCommand::with_name("status").about("Show current status")) - .subcommand( - SubCommand::with_name("config") - .about("Change config values") - .arg(Arg::with_name("name").required(true)) - .arg(Arg::with_name("value").required(true)), - ) - .subcommand( - SubCommand::with_name("config-reload").about("Force a reload of the config file"), - ) - .subcommand( - SubCommand::with_name("completions") - .about("Generate CLI completions") - .arg(Arg::with_name("zsh").long("zsh")) - .arg(Arg::with_name("bash").long("bash")) - .arg(Arg::with_name("fish").long("fish")), - ) - .subcommand( - SubCommand::with_name("volume") - .about("Change volume of either you or someone else") - .subcommand( - SubCommand::with_name("set") - .arg(Arg::with_name("user").required(true)) - .arg(Arg::with_name("volume").required(true)), - ) - .arg(Arg::with_name("user").required(true)) - .setting(AppSettings::SubcommandsNegateReqs), - ) - .subcommand( - SubCommand::with_name("mute") - .about("Mute someone/yourself") - .arg(Arg::with_name("user")) - ) - .subcommand( - SubCommand::with_name("unmute") - .about("Unmute someone/yourself") - .arg(Arg::with_name("user")) - ) - .subcommand( - SubCommand::with_name("deafen") - .about("Deafen yourself") - ) - .subcommand( - SubCommand::with_name("undeafen") - .about("Undeafen yourself") - ); + }; - let matches = app.clone().get_matches(); - - if let Err(e) = process_matches(matches, &mut config, &mut app) { - error!("{}", e); + let response = send_command(MumCommand::ServerConnect { + host: host.to_string(), + port, + username: username.to_string(), + password: password.map(|x| x.to_string()), + accept_invalid_cert: true //TODO + })??; + if let Some(CommandResponse::ServerConnect { welcome_message }) = response { + println!("Connected to {}", host); + if let Some(message) = welcome_message { + println!("Welcome: {}", message); + } + } + } + Command::Disconnect => { + send_command(MumCommand::ServerDisconnect)??; + } + Command::Server(server_command) => { + match_server_command(server_command, &mut config)?; + } + Command::Channel(channel_command) => { + match channel_command { + Channel::List { short: _short } => { //TODO actually use this + match send_command(MumCommand::ChannelList)?? { + Some(CommandResponse::ChannelList { channels }) => { + print_channel(&channels, 0); + } + _ => unreachable!("Response should only be a ChannelList"), + } + } + Channel::Connect { name } => { + send_command(MumCommand::ChannelJoin { + channel_identifier: name + })??; + } + } + } + Command::Status => { + match send_command(MumCommand::Status)?? { + Some(CommandResponse::Status { server_state }) => { + parse_state(&server_state); + } + _ => unreachable!("Response should only be a Status"), + } + } + Command::Config { key, value } => { + match key.as_str() { + "audio.input_volume" => { + if let Ok(volume) = value.parse() { + send_command(MumCommand::InputVolumeSet(volume))??; + config.audio.input_volume = Some(volume); + } + } + "audio.output_volume" => { + if let Ok(volume) = value.parse() { + send_command(MumCommand::OutputVolumeSet(volume))??; + config.audio.output_volume = Some(volume); + } + } + _ => { + return Err(CliError::ConfigKeyNotFound(key))?; + } + } + } + Command::ConfigReload => { + send_command(MumCommand::ConfigReload)??; + } + Command::Completions(completions) => { + Mum::clap().gen_completions_to( + "mumctl", + match completions { + Completions::Bash => Shell::Bash, + Completions::Fish => Shell::Fish, + _ => Shell::Zsh, + }, + &mut io::stdout(), + ); + } + Command::Volume { user, volume } => { + if let Some(volume) = volume { + send_command(MumCommand::UserVolumeSet(user, volume))??; + } else { + //TODO report volume of user + // needs work on mumd + todo!(); + } + } + Command::Mute { user } => { + match user { + Some(user) => { + send_command(MumCommand::MuteOther(user, Some(true)))??; + } + None => { + send_command(MumCommand::MuteSelf(Some(true)))??; + } + } + } + Command::Unmute { user } => { + match user { + Some(user) => { + send_command(MumCommand::MuteOther(user, Some(false)))??; + } + None => { + send_command(MumCommand::MuteSelf(Some(false)))??; + } + } + } + Command::Deafen => { + send_command(MumCommand::DeafenSelf(Some(true)))??; + } + Command::Undeafen => { + send_command(MumCommand::DeafenSelf(Some(false)))??; + } } if !config::cfg_exists() { @@ -202,31 +338,123 @@ fn main() { } else { error_if_err!(config.write_default_cfg(false)); } + Ok(()) } -fn process_matches(matches: ArgMatches, config: &mut Config, app: &mut App) -> Result<(), Error> { - if let Some(matches) = matches.subcommand_matches("connect") { - match_server_connect(matches, &config)?; - } else if let Some(_) = matches.subcommand_matches("disconnect") { - error_if_err!(send_command(Command::ServerDisconnect)?); - } else if let Some(matches) = matches.subcommand_matches("server") { - if let Some(matches) = matches.subcommand_matches("config") { - match_server_config(matches, config); - } else if let Some(matches) = matches.subcommand_matches("rename") { - match_server_rename(matches, config); - } else if let Some(matches) = matches.subcommand_matches("remove") { - match_server_remove(matches, config); - } else if let Some(matches) = matches.subcommand_matches("add") { - match_server_add(matches, config); - } else if let Some(_) = matches.subcommand_matches("list") { +fn match_server_command(server_command: Server, config: &mut Config) -> Result<()> { + match server_command { + Server::Config { server_name, key, value } => { + let server_name = match server_name { + Some(server_name) => server_name, + None => { + for server in config.servers.iter() { + println!("{}", server.name); + } + return Ok(()); + } + }; + let server = config.servers + .iter_mut() + .find(|s| s.name == server_name) + .ok_or(CliError::NoServerFound(server_name))?; + + match (key.as_deref(), value) { + (None, _) => { + print!( + "{}{}{}{}", + format!("host: {}\n", server.host.to_string()), + server + .port + .map(|s| format!("port: {}\n", s)) + .unwrap_or_else(|| "".to_string()), + server + .username + .as_ref() + .map(|s| format!("username: {}\n", s)) + .unwrap_or_else(|| "".to_string()), + server + .password + .as_ref() + .map(|s| format!("password: {}\n", s)) + .unwrap_or_else(|| "".to_string()), + ); + } + (Some("name"), None) => { + println!("{}", server.name); + } + (Some("host"), None) => { + println!("{}", server.host); + } + (Some("port"), None) => { + println!("{}", + server + .port + .ok_or(CliError::NotSet("port".to_string()))?); + } + (Some("username"), None) => { + println!("{}", + server + .username + .as_ref() + .ok_or(CliError::NotSet("username".to_string()))?); + } + (Some("password"), None) => { + println!("{}", + server + .password + .as_ref() + .ok_or(CliError::NotSet("password".to_string()))?); + } + (Some("name"), Some(_)) => { + return Err(CliError::UseServerRename)?; + } + (Some("host"), Some(value)) => { + server.host = value; + } + (Some("port"), Some(value)) => { + server.port = Some(value.parse().unwrap()); + } + (Some("username"), Some(value)) => { + server.username = Some(value); + } + (Some("password"), Some(value)) => { + server.password = Some(value); + //TODO ask stdin if empty + } + (Some(_), _) => { + return Err(CliError::ConfigKeyNotFound(key.unwrap()))?; + } + } + } + Server::Rename { old_name, new_name } => { + config.servers.iter_mut().find(|s| s.name == old_name).ok_or(CliError::NoServerFound(old_name))?.name = new_name; + } + Server::Add { name, host, port, username, password } => { + if config.servers.iter().any(|s| s.name == name) { + return Err(CliError::ServerAlreadyExists(name))?; + } else { + config.servers.push(ServerConfig { + name, + host, + port, + username, + password, + }); + } + } + Server::Remove { name } => { + let idx = config.servers.iter().position(|s| s.name == name).ok_or(CliError::NoServerFound(name))?; + config.servers.remove(idx); + } + Server::List => { if config.servers.is_empty() { - warn!("No servers in config"); + return Err(CliError::NoServers)?; } let query = config .servers .iter() .map(|e| { - let response = send_command(Command::ServerStatus { + let response = send_command(MumCommand::ServerStatus { host: e.host.clone(), port: e.port.unwrap_or(mumlib::DEFAULT_PORT), }); @@ -246,314 +474,12 @@ fn process_matches(matches: ArgMatches, config: &mut Config, app: &mut App) -> R } } } - } else if let Some(matches) = matches.subcommand_matches("channel") { - if let Some(_matches) = matches.subcommand_matches("list") { - match send_command(Command::ChannelList)? { - Ok(res) => match res { - Some(CommandResponse::ChannelList { channels }) => { - print_channel(&channels, 0); - } - _ => unreachable!(), - }, - Err(e) => error!("{}", e), - } - } else if let Some(matches) = matches.subcommand_matches("connect") { - error_if_err!(send_command(Command::ChannelJoin { - channel_identifier: matches.value_of("channel").unwrap().to_string() - })); - } - } else if let Some(_) = matches.subcommand_matches("status") { - match send_command(Command::Status)? { - Ok(res) => match res { - Some(CommandResponse::Status { server_state }) => { - parse_status(&server_state); - } - _ => unreachable!(), - }, - Err(e) => error!("{}", e), - } - } else if let Some(matches) = matches.subcommand_matches("config") { - let name = matches.value_of("name").unwrap(); - let value = matches.value_of("value").unwrap(); - match name { - "audio.input_volume" => { - if let Ok(volume) = value.parse() { - error_if_err!(send_command(Command::InputVolumeSet(volume))?); - config.audio.input_volume = Some(volume); - } - } - "audio.output_volume" => { - if let Ok(volume) = value.parse() { - error_if_err!(send_command(Command::OutputVolumeSet(volume))?); - config.audio.output_volume = Some(volume); - } - } - _ => { - error!("unknown config value {}", name); - } - } - } else if matches.subcommand_matches("config-reload").is_some() { - error_if_err!(send_command(Command::ConfigReload)?); - } 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(), - ); - } else if let Some(matches) = matches.subcommand_matches("volume") { - if let Some(matches) = matches.subcommand_matches("set") { - let user = matches.value_of("user").unwrap(); - let volume = matches.value_of("volume").unwrap(); - if let Ok(val) = volume.parse() { - error_if_err!(send_command(Command::UserVolumeSet(user.to_string(), val))) - } else { - error!("invalid volume value: {}", volume); - } - } else { - let _user = matches.value_of("user").unwrap(); - //TODO implement me - //needs work on mumd to implement - } - } else if let Some(matches) = matches.subcommand_matches("mute") { - let command = if let Some(user) = matches.value_of("user") { - Command::MuteOther(user.to_string(), Some(true)) - } else { - Command::MuteSelf(Some(true)) - }; - if let Err(e) = send_command(command)? { - error!("{}", e); - } - } else if let Some(matches) = matches.subcommand_matches("unmute") { - let command = if let Some(user) = matches.value_of("user") { - Command::MuteOther(user.to_string(), Some(false)) - } else { - Command::MuteSelf(Some(false)) - }; - if let Err(e) = send_command(command)? { - error!("{}", e); - } - } else if let Some(_) = matches.subcommand_matches("deafen") { - if let Err(e) = send_command(Command::DeafenSelf(Some(true)))? { - error!("{}", e); - } - } else if let Some(_) = matches.subcommand_matches("undeafen") { - if let Err(e) = send_command(Command::DeafenSelf(Some(false)))? { - error!("{}", e); - } - } else { - unreachable!(); } - Ok(()) } -fn match_server_connect(matches: &clap::ArgMatches<'_>, config: &mumlib::config::Config) -> Result<(), Error> { - let host = matches.value_of("host").unwrap(); - let username = matches.value_of("username"); - let password = matches.value_of("password"); - let port = match matches.value_of("port").map(|e| e.parse()) { - None => Some(mumlib::DEFAULT_PORT), - Some(Err(_)) => None, - Some(Ok(v)) => Some(v), - }; - if let Some(port) = port { - let (host, port, username, password) = match config.servers.iter().find(|e| e.name == host) { - Some(server_config) => { - let host = server_config.host.as_str(); - let port = server_config.port.unwrap_or(port); - let username = server_config - .username - .as_deref() - .or(username); - if username.is_none() { - error!("no username specified"); - return Ok(()); //TODO? return as error - } - let password = server_config - .password - .as_deref() - .or(password); - (host, port, username.unwrap(), password) - } - None => { - if username.is_none() { - error!("no username specified"); - return Ok(()); //TODO? return as error - } - (host, port, username.unwrap(), password) - } - }; - let response = send_command(Command::ServerConnect { - host: host.to_string(), - port, - username: username.to_string(), - password: password.map(|x| x.to_string()), - accept_invalid_cert: true, //TODO - })? - .map(|e| (e, host)); - match response { - Ok((e, host)) => { - if let Some(CommandResponse::ServerConnect { welcome_message }) = e { - println!("Connected to {}", host); - if let Some(message) = welcome_message { - println!("Welcome: {}", message); - } - } - } - Err(e) => { - error!("{}", e); - } - }; - } - - Ok(()) -} - -fn match_server_config(matches: &clap::ArgMatches<'_>, config: &mut mumlib::config::Config) { - if let Some(server_name) = matches.value_of("server_name") { - let server = config.servers.iter_mut().find(|s| s.name == server_name); - if let Some(server) = server { - if let Some(var_name) = matches.value_of("var_name") { - if let Some(var_value) = matches.value_of("var_value") { - // save var_value in var_name (if it is valid) - match var_name { - "name" => { - error!("use mumctl server rename instead!"); - } - "host" => { - server.host = var_value.to_string(); - } - "port" => { - server.port = Some(var_value.parse().unwrap()); - } - "username" => { - server.username = Some(var_value.to_string()); - } - "password" => { - server.password = Some(var_value.to_string()); //TODO ask stdin if empty - } - _ => { - error!("variable {} not found", var_name); - } - }; - } else { - // var_value is None - // print value of var_name - println!( - "{}", - match var_name { - "name" => { - server.name.to_string() - } - "host" => { - server.host.to_string() - } - "port" => { - server - .port - .map(|s| s.to_string()) - .unwrap_or(format!("{} not set", "error:".red())) - } - "username" => { - server - .username - .as_ref() - .map(|s| s.to_string()) - .unwrap_or(format!("{} not set", "error:".red())) - } - "password" => { - server - .password - .as_ref() - .map(|s| s.to_string()) - .unwrap_or(format!("{} not set", "error:".red())) - } - _ => { - format!("{} unknown variable", "error:".red()) - } - } - ); - } - } else { - // var_name is None - // print server config - print!( - "{}{}{}{}", - format!("host: {}\n", server.host.to_string()), - server - .port - .map(|s| format!("port: {}\n", s)) - .unwrap_or_else(|| "".to_string()), - server - .username - .as_ref() - .map(|s| format!("username: {}\n", s)) - .unwrap_or_else(|| "".to_string()), - server - .password - .as_ref() - .map(|s| format!("password: {}\n", s)) - .unwrap_or_else(|| "".to_string()), - ) - } - } else { - // server is None - error!("server {} not found", server_name); - } - } else { - for server in config.servers.iter() { - println!("{}", server.name); - } - } -} - -fn match_server_rename(matches: &clap::ArgMatches<'_>, config: &mut mumlib::config::Config) { - let prev_name = matches.value_of("prev_name").unwrap(); - let next_name = matches.value_of("next_name").unwrap(); - if let Some(server) = config.servers.iter_mut().find(|s| s.name == prev_name) { - server.name = next_name.to_string(); - } else { - error!("server {} not found", prev_name); - } -} - -fn match_server_remove(matches: &clap::ArgMatches<'_>, config: &mut mumlib::config::Config) { - let name = matches.value_of("name").unwrap(); - match config.servers.iter().position(|server| server.name == name) { - Some(idx) => { - config.servers.remove(idx); - } - None => { - error!("server {} not found", name); - } - }; -} -fn match_server_add(matches: &clap::ArgMatches<'_>, config: &mut mumlib::config::Config) { - let name = matches.value_of("name").unwrap().to_string(); - let host = matches.value_of("host").unwrap().to_string(); - // optional arguments map None to None - let port = matches.value_of("port").map(|s| s.parse().unwrap()); - let username = matches.value_of("username").map(|s| s.to_string()); - let password = matches.value_of("password").map(|s| s.to_string()); - if config.servers.iter().any(|s| s.name == name) { - error!("a server named {} already exists", name); - } else { - config.servers.push(ServerConfig { - name, - host, - port, - username, - password, - }); - } -} - -fn parse_status(server_state: &mumlib::state::Server) { +fn parse_state(server_state: &mumlib::state::Server) { println!( "Connected to {} as {}", server_state.host, server_state.username @@ -579,19 +505,19 @@ fn parse_status(server_state: &mumlib::state::Server) { } } -fn send_command(command: Command) -> Result<mumlib::error::Result<Option<CommandResponse>>, crate::Error> { - let mut connection = UnixStream::connect(mumlib::SOCKET_PATH).map_err(|_| Error::ConnectionError)?; +fn send_command(command: MumCommand) -> Result<mumlib::error::Result<Option<CommandResponse>>, CliError> { + let mut connection = UnixStream::connect(mumlib::SOCKET_PATH).map_err(|_| CliError::ConnectionError)?; let serialized = bincode::serialize(&command).unwrap(); - connection.write(&(serialized.len() as u32).to_be_bytes()).map_err(|_| Error::ConnectionError)?; - connection.write(&serialized).map_err(|_| Error::ConnectionError)?; + connection.write(&(serialized.len() as u32).to_be_bytes()).map_err(|_| CliError::ConnectionError)?; + connection.write(&serialized).map_err(|_| CliError::ConnectionError)?; - connection.read_exact(&mut [0; 4]).map_err(|_| Error::ConnectionError)?; - bincode::deserialize_from(&mut connection).map_err(|_| Error::ConnectionError) + connection.read_exact(&mut [0; 4]).map_err(|_| CliError::ConnectionError)?; + bincode::deserialize_from(&mut connection).map_err(|_| CliError::ConnectionError) } -fn print_channel(channel: &Channel, depth: usize) { +fn print_channel(channel: &MumChannel, depth: usize) { println!( "{}{}{}", iter::repeat(INDENTATION).take(depth).collect::<String>(), @@ -615,14 +541,3 @@ fn print_channel(channel: &Channel, depth: usize) { print_channel(child, depth + 1); } } - -#[derive(Debug)] -enum Error { - ConnectionError -} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "Unable to connect to mumd. Is mumd running?") - } -} diff --git a/mumlib/Cargo.toml b/mumlib/Cargo.toml index 43bd8c5..183ebac 100644 --- a/mumlib/Cargo.toml +++ b/mumlib/Cargo.toml @@ -11,9 +11,9 @@ repository = "https://github.com/sornas/mum" license = "MIT" [dependencies] -colored = "2.0" +colored = "2" dirs = "3" fern = "0.6" log = "0.4" -serde = { version = "1.0", features = ["derive"] } +serde = { version = "1", features = ["derive"] } toml = "0.5" diff --git a/mumlib/src/error.rs b/mumlib/src/error.rs index 6b7dccd..459ede0 100644 --- a/mumlib/src/error.rs +++ b/mumlib/src/error.rs @@ -13,6 +13,8 @@ pub enum Error { InvalidServerPassword, } +impl std::error::Error for Error {} + impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -43,6 +45,9 @@ impl fmt::Display for ChannelIdentifierError { } } +impl std::error::Error for ChannelIdentifierError {} + +#[derive(Debug)] pub enum ConfigError { InvalidConfig, TOMLErrorSer(toml::ser::Error), @@ -52,6 +57,8 @@ pub enum ConfigError { IOError(std::io::Error), } +impl std::error::Error for ConfigError {} + impl fmt::Display for ConfigError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { |
