diff --git a/src/main.rs b/src/main.rs index 214e672..1f7245b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,9 @@ -use clap::builder::Str; use clap::{Parser, Subcommand, ValueEnum}; use std::env; +use std::error::Error; +use std::fmt::{Display, Formatter}; use std::path::PathBuf; +use std::str::FromStr; const SERVERS_ENV_VAR: &str = "MSSH_SERVERS"; @@ -20,8 +22,8 @@ struct Args { #[command(subcommand)] command: Command, /// The ssh names and optionally home directories of the servers to perform the action on - #[arg(num_args = 1..)] - servers: Vec, //TODO directly parse servers and their directories + #[arg(num_args = 1.., value_parser = ServerReference::from_str)] + servers: Vec, } #[derive(Subcommand, Debug)] @@ -54,12 +56,81 @@ enum OldVersionPolicy { Delete, } +#[derive(Debug, Clone)] +enum ServerReference { + Resolved(Server), + Name(String), +} + +impl ServerReference { + //TODO lazy resolve method which gets a provider for configured servers + pub fn resolve(self, configured_servers: &[Server]) -> Option { + match self { + ServerReference::Resolved(server) => Some(server), + ServerReference::Name(name) => configured_servers + .iter() + .find(|server| server.ssh_name == name) + .cloned(), + } + } +} + +impl FromStr for ServerReference { + type Err = ServerParseError; + + fn from_str(s: &str) -> Result { + Server::from_str(s) + .map(Self::Resolved) + .or_else(|_| Ok(Self::Name(s.to_string()))) + } +} + #[derive(Debug, Clone, Eq, PartialEq, Hash)] struct Server { pub ssh_name: String, pub server_directory_path: PathBuf, } +impl FromStr for Server { + type Err = ServerParseError; + + fn from_str(s: &str) -> Result { + s.split_once(':') + .ok_or(ServerParseError::MissingServerDirectory) + .map(|(name, directory)| Self { + ssh_name: name.to_string(), + server_directory_path: PathBuf::from(directory), + }) + } +} + +impl Display for Server { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}:{:?}", self.ssh_name, self.server_directory_path) + } +} + +#[derive(Debug)] +enum ServerParseError { + MissingServerDirectory, +} + +impl Display for ServerParseError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + ServerParseError::MissingServerDirectory => { + write!( + f, + "String is not specifying a server directory. Please use an empty string after a \ + double colon to point to the home directory, e.g: 'lobby:'" + ) + } + } + } +} + +impl Error for ServerParseError {} + #[derive(Debug, Clone, Eq, PartialEq, Hash)] struct PluginInfo { pub name: String, @@ -73,19 +144,6 @@ fn main() -> Result<(), String> { let configured_servers = parse_server_configuration_from_env()?; dbg!(&configured_servers); - let servers = args - .servers - .iter() - .map(|server_name| { - configured_servers - .iter() - .find(|server| server.ssh_name == *server_name) - .ok_or(format!( - "no server with the name '{server_name}' has been configured" - )) - }) - .collect::, _>>()?; - Ok(()) } @@ -96,13 +154,13 @@ fn parse_server_configuration_from_env() -> Result, String> { } fn parse_server_configuration(config_str: &str) -> Result, String> { - config_str.split(',').map(|server_entry| { - let (name, server_directory) = server_entry.split_once(':').ok_or(format!("Server entry doesn't specify a server directory (separate name and directory via double colon) for entry '{}'", server_entry))?; - Ok(Server { - ssh_name: name.to_string(), - server_directory_path: PathBuf::from(server_directory), + config_str + .split(',') + .map(|server_entry| { + Server::from_str(server_entry) + .map_err(|e| format!("Invalid server entry '{server_entry}': {e}")) }) - }).collect() + .collect() } #[cfg(test)]