From d85c652d8f5e3c01fec988cc77f2e199dc2f3dce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Sat, 3 Apr 2021 11:09:06 +0200 Subject: clap -> structopt I tried to translate the current clap-code into structopt and save any modifications for later, /but/, some things would've taken too much energy. But I've forgotten what those were. --- mumctl/src/main.rs | 883 ++++++++++++++++++++++++----------------------------- 1 file changed, 399 insertions(+), 484 deletions(-) (limited to 'mumctl/src') 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, + password: Option, + port: Option, + }, + /// 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, + }, + /// Mute someone/yourself + Mute { + user: Option, + }, + /// Unmute someone/yourself + Unmute { + user: Option, + }, + /// Deafen yourself + Deafen, + /// Undeafen yourself + Undeafen, +} + +#[derive(Debug, StructOpt)] +enum Server { + /// Configure a saved server + Config { + server_name: Option, + key: Option, + value: Option, + }, + /// Rename a saved server + Rename { + old_name: String, + new_name: String + }, + /// Add a new saved server + Add { + name: String, + host: String, + port: Option, + username: Option, + password: Option, + }, + /// 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>, crate::Error> { - let mut connection = UnixStream::connect(mumlib::SOCKET_PATH).map_err(|_| Error::ConnectionError)?; +fn send_command(command: MumCommand) -> Result>, 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::(), @@ -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?") - } -} -- cgit v1.2.1 From e173cb3bfee0a5aad1a615957e4519c63519c09e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Sat, 3 Apr 2021 11:29:45 +0200 Subject: add structopt flag directives --- mumctl/src/main.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'mumctl/src') diff --git a/mumctl/src/main.rs b/mumctl/src/main.rs index 95b98f2..42226e3 100644 --- a/mumctl/src/main.rs +++ b/mumctl/src/main.rs @@ -55,6 +55,7 @@ enum Command { host: String, username: Option, password: Option, + #[structopt(short = "p", long = "port")] port: Option, }, /// Disconnect from the currently connected server @@ -110,8 +111,10 @@ enum Server { Add { name: String, host: String, + #[structopt(short = "p", long = "port")] port: Option, username: Option, + #[structopt(requires = "username")] password: Option, }, /// Remove a saved server -- cgit v1.2.1 From 1849a9473dde0e735538a85e5a6d7f4a6a5601ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Sat, 3 Apr 2021 11:54:43 +0200 Subject: more anyhow --- mumctl/src/main.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'mumctl/src') diff --git a/mumctl/src/main.rs b/mumctl/src/main.rs index 42226e3..28add5d 100644 --- a/mumctl/src/main.rs +++ b/mumctl/src/main.rs @@ -9,14 +9,6 @@ use structopt::{clap::Shell, StructOpt}; const INDENTATION: &str = " "; -macro_rules! error_if_err { - ($func:expr) => { - if let Err(e) = $func { - error!("{}", e); - } - }; -} - struct SimpleLogger; impl log::Log for SimpleLogger { @@ -336,10 +328,10 @@ fn main() -> Result<()> { let stdin = std::io::stdin(); let response = stdin.lock().lines().next(); if let Some(Ok(true)) = response.map(|e| e.map(|e| &e == "Y")) { - error_if_err!(config.write_default_cfg(true)); + config.write_default_cfg(true)?; } } else { - error_if_err!(config.write_default_cfg(false)); + config.write_default_cfg(false)?; } Ok(()) } -- cgit v1.2.1 From afb5e9f40786e67d207d6ad2ad9ee09d7f76ee87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Sat, 3 Apr 2021 13:02:22 +0200 Subject: cargo fmt --- mumctl/src/main.rs | 222 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 133 insertions(+), 89 deletions(-) (limited to 'mumctl/src') diff --git a/mumctl/src/main.rs b/mumctl/src/main.rs index 28add5d..4ce8016 100644 --- a/mumctl/src/main.rs +++ b/mumctl/src/main.rs @@ -4,7 +4,12 @@ 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 std::{ + fmt::Formatter, + io::{self, BufRead, Read, Write}, + iter, + os::unix::net::UnixStream, +}; use structopt::{clap::Shell, StructOpt}; const INDENTATION: &str = " "; @@ -25,7 +30,8 @@ impl log::Log for SimpleLogger { Level::Warn => "warning: ".yellow(), _ => "".normal(), }, - record.args()); + record.args() + ); } } @@ -182,35 +188,46 @@ impl std::fmt::Display for CliError { fn main() -> Result<()> { log::set_logger(&LOGGER) - .map(|()| log::set_max_level(LevelFilter::Info)).unwrap(); + .map(|()| log::set_max_level(LevelFilter::Info)) + .unwrap(); let mut config = config::read_default_cfg()?; let opt = Mum::from_args(); match opt.command { - Command::Connect { host, username, password, port } => { + 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), - ), - None => ( - &host, - username.as_ref().ok_or(CliError::NoUsername)?, - password.as_ref(), - 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), + ), + None => ( + &host, + username.as_ref().ok_or(CliError::NoUsername)?, + password.as_ref(), + port, + ), + }; 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 + accept_invalid_cert: true, //TODO })??; if let Some(CommandResponse::ServerConnect { welcome_message }) = response { println!("Connected to {}", host); @@ -227,7 +244,8 @@ fn main() -> Result<()> { } Command::Channel(channel_command) => { match channel_command { - Channel::List { short: _short } => { //TODO actually use this + Channel::List { short: _short } => { + //TODO actually use this match send_command(MumCommand::ChannelList)?? { Some(CommandResponse::ChannelList { channels }) => { print_channel(&channels, 0); @@ -237,38 +255,34 @@ fn main() -> Result<()> { } Channel::Connect { name } => { send_command(MumCommand::ChannelJoin { - channel_identifier: name + 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::Status => match send_command(MumCommand::Status)?? { + Some(CommandResponse::Status { server_state }) => { + parse_state(&server_state); } - } - 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); - } + _ => 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))?; + } + "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)??; } @@ -292,26 +306,22 @@ fn main() -> Result<()> { todo!(); } } - Command::Mute { user } => { - match user { - Some(user) => { - send_command(MumCommand::MuteOther(user, Some(true)))??; - } - None => { - send_command(MumCommand::MuteSelf(Some(true)))??; - } + Command::Mute { user } => match user { + Some(user) => { + send_command(MumCommand::MuteOther(user, Some(true)))??; } - } - Command::Unmute { user } => { - match user { - Some(user) => { - send_command(MumCommand::MuteOther(user, Some(false)))??; - } - None => { - send_command(MumCommand::MuteSelf(Some(false)))??; - } + 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)))??; } @@ -338,7 +348,11 @@ fn main() -> Result<()> { fn match_server_command(server_command: Server, config: &mut Config) -> Result<()> { match server_command { - Server::Config { server_name, key, value } => { + Server::Config { + server_name, + key, + value, + } => { let server_name = match server_name { Some(server_name) => server_name, None => { @@ -348,7 +362,8 @@ fn match_server_command(server_command: Server, config: &mut Config) -> Result<( return Ok(()); } }; - let server = config.servers + let server = config + .servers .iter_mut() .find(|s| s.name == server_name) .ok_or(CliError::NoServerFound(server_name))?; @@ -381,24 +396,28 @@ fn match_server_command(server_command: Server, config: &mut Config) -> Result<( println!("{}", server.host); } (Some("port"), None) => { - println!("{}", - server - .port - .ok_or(CliError::NotSet("port".to_string()))?); + 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()))?); + 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()))?); + println!( + "{}", + server + .password + .as_ref() + .ok_or(CliError::NotSet("password".to_string()))? + ); } (Some("name"), Some(_)) => { return Err(CliError::UseServerRename)?; @@ -422,9 +441,20 @@ fn match_server_command(server_command: Server, config: &mut Config) -> Result<( } } 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; + 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 } => { + Server::Add { + name, + host, + port, + username, + password, + } => { if config.servers.iter().any(|s| s.name == name) { return Err(CliError::ServerAlreadyExists(name))?; } else { @@ -438,7 +468,11 @@ fn match_server_command(server_command: Server, config: &mut Config) -> Result<( } } Server::Remove { name } => { - let idx = config.servers.iter().position(|s| s.name == name).ok_or(CliError::NoServerFound(name))?; + let idx = config + .servers + .iter() + .position(|s| s.name == name) + .ok_or(CliError::NoServerFound(name))?; config.servers.remove(idx); } Server::List => { @@ -456,9 +490,11 @@ fn match_server_command(server_command: Server, config: &mut Config) -> Result<( response.map(|f| (e, f)) }) .collect::, _>>()?; - for (server, response) in query.into_iter() + for (server, response) in query + .into_iter() .filter(|e| e.1.is_ok()) - .map(|e| (e.0, e.1.unwrap().unwrap())) { + .map(|e| (e.0, e.1.unwrap().unwrap())) + { if let CommandResponse::ServerStatus { users, max_users, .. } = response @@ -473,7 +509,6 @@ fn match_server_command(server_command: Server, config: &mut Config) -> Result<( Ok(()) } - fn parse_state(server_state: &mumlib::state::Server) { println!( "Connected to {} as {}", @@ -500,15 +535,24 @@ fn parse_state(server_state: &mumlib::state::Server) { } } -fn send_command(command: MumCommand) -> Result>, CliError> { - let mut connection = UnixStream::connect(mumlib::SOCKET_PATH).map_err(|_| CliError::ConnectionError)?; +fn send_command( + command: MumCommand, +) -> Result>, 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(|_| CliError::ConnectionError)?; - connection.write(&serialized).map_err(|_| CliError::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(|_| CliError::ConnectionError)?; + connection + .read_exact(&mut [0; 4]) + .map_err(|_| CliError::ConnectionError)?; bincode::deserialize_from(&mut connection).map_err(|_| CliError::ConnectionError) } -- cgit v1.2.1 From fc85f34301a99e404433650f3f4cd4d4ed24ec3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Tue, 6 Apr 2021 20:40:44 +0200 Subject: remove anyhow --- mumctl/src/main.rs | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) (limited to 'mumctl/src') diff --git a/mumctl/src/main.rs b/mumctl/src/main.rs index 4ce8016..0314cab 100644 --- a/mumctl/src/main.rs +++ b/mumctl/src/main.rs @@ -1,15 +1,9 @@ -use anyhow::Result; use colored::Colorize; use log::*; use mumlib::command::{Command as MumCommand, CommandResponse}; use mumlib::config::{self, Config, ServerConfig}; use mumlib::state::Channel as MumChannel; -use std::{ - fmt::Formatter, - io::{self, BufRead, Read, Write}, - iter, - os::unix::net::UnixStream, -}; +use std::{fmt,io::{self, BufRead, Read, Write}, iter, os::unix::net::UnixStream}; use structopt::{clap::Shell, StructOpt}; const INDENTATION: &str = " "; @@ -153,10 +147,28 @@ enum CliError { NoServers, } +#[derive(Debug)] +struct Error(Box); // new type since otherwise we'd get an impl conflict with impl From for T below + +impl From for Error +where + E: std::error::Error + fmt::Display + 'static, +{ + fn from(e: E) -> Self { + Error(Box::new(e)) + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + std::fmt::Display::fmt(&self.0, f) + } +} + impl std::error::Error for CliError {} -impl std::fmt::Display for CliError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { +impl fmt::Display for CliError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { CliError::NoUsername => { write!(f, "No username specified") @@ -168,7 +180,7 @@ impl std::fmt::Display for CliError { write!(f, "Server '{}' not found", s) } CliError::NotSet(s) => { - write!(f, "Key '{}' not set", s) //TODO + write!(f, "Key '{}' not set", s) } CliError::UseServerRename => { write!(f, "Use 'server rename' instead") @@ -186,10 +198,15 @@ impl std::fmt::Display for CliError { } } -fn main() -> Result<()> { +fn main() { log::set_logger(&LOGGER) .map(|()| log::set_max_level(LevelFilter::Info)) .unwrap(); + if let Err(e) = match_opt() { + error!("{}", e); + } +} +fn match_opt() -> Result<(), Error> { let mut config = config::read_default_cfg()?; let opt = Mum::from_args(); @@ -346,7 +363,7 @@ fn main() -> Result<()> { Ok(()) } -fn match_server_command(server_command: Server, config: &mut Config) -> Result<()> { +fn match_server_command(server_command: Server, config: &mut Config) -> Result<(), CliError> { match server_command { Server::Config { server_name, -- cgit v1.2.1 From cd35ea952d0615793c08ad2c07f5d1245ab8f28f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Tue, 6 Apr 2021 20:41:40 +0200 Subject: remove std --- mumctl/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'mumctl/src') diff --git a/mumctl/src/main.rs b/mumctl/src/main.rs index 0314cab..29c9e44 100644 --- a/mumctl/src/main.rs +++ b/mumctl/src/main.rs @@ -161,7 +161,7 @@ where impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - std::fmt::Display::fmt(&self.0, f) + fmt::Display::fmt(&self.0, f) } } -- cgit v1.2.1