Merge pull request 'Allow localhost as server target' (#17) from 14-allow-locahost-as-server-target into master
Reviewed-on: https://stupstech.de/dev/Mr_Steppy/multi-ssh/pulls/17
This commit is contained in:
commit
aa1f53d225
@ -17,10 +17,15 @@ Ergo you should be able to connect to your desired servers via ssh and be able t
|
||||
### Environment variable setup example for linux
|
||||
|
||||
```bash
|
||||
export MSSH_SERVERS="crea:server/creative2,sky:sky,city:city2"
|
||||
export MSSH_SERVERS="crea:server/creative2,sky:sky,city:city2,.:minecraft-server"
|
||||
export MSSH_EDITOR="kate -b <file>" #<file> is the placeholder for the file name
|
||||
```
|
||||
|
||||
### localhost as server-target
|
||||
|
||||
You may also use `.` to refer to your local minecraft server, without having to open the ssh port. You still have to
|
||||
define the server directory though.
|
||||
|
||||
## Usage
|
||||
|
||||
For detailed usage please see:
|
||||
@ -80,4 +85,4 @@ and you will find an executable in `target/release`.
|
||||
|
||||
### Unit tests
|
||||
|
||||
In order for the unit tests to pass, you will need `python3`
|
||||
In order for the unit tests to pass, you will need `python3`.
|
||||
|
||||
@ -12,7 +12,7 @@ pub struct ServerActions<'a> {
|
||||
|
||||
impl Display for ServerActions<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}: ({})", self.server.ssh_name, self.working_directory.to_string_lossy())?;
|
||||
write!(f, "{}: ({})", self.server.get_name(), self.working_directory.to_string_lossy())?;
|
||||
for action in &self.actions {
|
||||
write!(f, "\n{}", action)?;
|
||||
}
|
||||
|
||||
@ -49,6 +49,7 @@ fn collect_output(
|
||||
let output = command.output()?; //pipes stdout and stderr automatically
|
||||
if !output.status.success() {
|
||||
if let Some(logger) = logger {
|
||||
log!(logger, error, "{}", String::from_utf8_lossy(&output.stdout));
|
||||
log!(logger, error, "{}", String::from_utf8_lossy(&output.stderr));
|
||||
}
|
||||
Err(output.status)?;
|
||||
|
||||
@ -7,14 +7,20 @@ pub struct Logger {
|
||||
|
||||
macro_rules! define_log_function {
|
||||
($name:ident, $level:ident) => {
|
||||
pub fn $name<S>(&self, message: S) where S: ToString {
|
||||
self.log(LogLevel::$level, message.to_string());
|
||||
}
|
||||
pub fn $name<S>(&self, message: S)
|
||||
where
|
||||
S: ToString,
|
||||
{
|
||||
self.log(LogLevel::$level, message.to_string());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Logger {
|
||||
pub fn log<S>(&self, level: LogLevel, message: S) where S: ToString {
|
||||
pub fn log<S>(&self, level: LogLevel, message: S)
|
||||
where
|
||||
S: ToString,
|
||||
{
|
||||
if level >= self.level {
|
||||
println!("{}", message.to_string());
|
||||
}
|
||||
@ -38,9 +44,10 @@ macro_rules! log {
|
||||
($logger:expr, $level:ident, $($args:tt)*) => {
|
||||
$logger.$level(format!($($args)*));
|
||||
};
|
||||
($logger:expr, $($args:tt)*) => {
|
||||
log!($logger, info, $($args)*); //TODO better use default level with log function instead of assuming info as default
|
||||
}
|
||||
($logger:expr, $($args:tt)*) => {{
|
||||
use $crate::logger::LogLevel;
|
||||
$logger.log(LogLevel::default(), format!($($args)*));
|
||||
}};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
161
src/main.rs
161
src/main.rs
@ -10,6 +10,7 @@ use crate::command::LogRunnable;
|
||||
use crate::file::{FileMatcher, FileNameInfo};
|
||||
use crate::logger::{LogLevel, Logger};
|
||||
use crate::os_string_builder::ReplaceWithOsStr;
|
||||
use crate::server::ServerAddress;
|
||||
use clap::{Parser, Subcommand, ValueEnum};
|
||||
use lazy_regex::{lazy_regex, Lazy, Regex};
|
||||
use server::{Server, ServerReference};
|
||||
@ -156,7 +157,7 @@ fn main() -> Result<(), String> {
|
||||
.servers
|
||||
.iter()
|
||||
.map(|server_reference| {
|
||||
let server_name = server_reference.get_name();
|
||||
let server_name = server_reference.get_identifier();
|
||||
server_reference
|
||||
.clone()
|
||||
.try_resolve_lazy(&mut configured_servers)
|
||||
@ -191,12 +192,22 @@ fn main() -> Result<(), String> {
|
||||
Ok(ServerActions {
|
||||
server,
|
||||
actions: {
|
||||
let output = ShellCmd::new("ssh")
|
||||
.arg(&server.ssh_name)
|
||||
.arg(osf!("ls ") + &working_directory)
|
||||
let mut ls_command = match &server.address {
|
||||
ServerAddress::Ssh { ssh_address } => {
|
||||
let mut cmd = ShellCmd::new("ssh");
|
||||
cmd.arg(ssh_address).arg(osf!("ls ") + &working_directory);
|
||||
cmd
|
||||
}
|
||||
ServerAddress::Localhost => {
|
||||
let mut cmd = ShellCmd::new("ls");
|
||||
cmd.arg(&working_directory);
|
||||
cmd
|
||||
}
|
||||
};
|
||||
let ls_output = ls_command
|
||||
.collect_output()
|
||||
.map_err(|e| format!("failed to query files: {e}"))?;
|
||||
let output = String::from_utf8_lossy(&output.stdout);
|
||||
let output = String::from_utf8_lossy(&ls_output.stdout);
|
||||
|
||||
let mut file_matcher =
|
||||
FileMatcher::from(file_name.as_ref().unwrap_or(&file_name_info.name));
|
||||
@ -279,37 +290,52 @@ fn main() -> Result<(), String> {
|
||||
|
||||
for server_actions in actions {
|
||||
let server = server_actions.server;
|
||||
log!(logger, "Performing actions on {}...", server.ssh_name);
|
||||
log!(logger, "Performing actions on {}...", server.get_name());
|
||||
for file_action in server_actions.actions {
|
||||
match file_action.kind {
|
||||
Action::Add | Action::Replace => {
|
||||
let scp_target = osf!(match &server.address {
|
||||
ServerAddress::Ssh { ssh_address } => format!("{ssh_address}:"),
|
||||
ServerAddress::Localhost => "".to_string(),
|
||||
}) + &server_actions.working_directory;
|
||||
ShellCmd::new("scp")
|
||||
.arg(file.clone())
|
||||
.arg(osf!(&server.ssh_name) + ":" + &server_actions.working_directory)
|
||||
.arg(scp_target)
|
||||
.run(&logger)
|
||||
.map_err(|e| format!("upload failure: {e}"))?;
|
||||
}
|
||||
Action::Delete => {
|
||||
ShellCmd::new("ssh")
|
||||
.arg(&server.ssh_name)
|
||||
.arg(osf!("cd ") + &server_actions.working_directory + "; rm " + &file_action.file)
|
||||
.run(&logger)
|
||||
.map_err(|e| format!("failed to delete old version: {e}"))?;
|
||||
}
|
||||
Action::Rename { new_name } => {
|
||||
ShellCmd::new("ssh")
|
||||
.arg(&server.ssh_name)
|
||||
.arg(
|
||||
osf!("cd ")
|
||||
+ &server_actions.working_directory
|
||||
+ "; mv "
|
||||
+ &file_action.file
|
||||
+ " "
|
||||
+ new_name,
|
||||
)
|
||||
.run(&logger)
|
||||
.map_err(|e| format!("failed to rename: {e}"))?;
|
||||
}
|
||||
Action::Delete => match &server.address {
|
||||
ServerAddress::Ssh { ssh_address } => {
|
||||
ShellCmd::new("ssh")
|
||||
.arg(ssh_address)
|
||||
.arg(osf!("rm ") + server_actions.working_directory.join(&file_action.file))
|
||||
.run(&logger)
|
||||
.map_err(|e| format!("failed to delete old version: {e}"))?;
|
||||
}
|
||||
ServerAddress::Localhost => {
|
||||
fs::remove_file(server_actions.working_directory.join(&file_action.file))
|
||||
.map_err(|e| format!("failed to delete old version: {e}"))?;
|
||||
}
|
||||
},
|
||||
Action::Rename { new_name } => match &server.address {
|
||||
ServerAddress::Ssh { ssh_address } => {
|
||||
ShellCmd::new("ssh")
|
||||
.arg(ssh_address)
|
||||
.arg(
|
||||
osf!("mv ")
|
||||
+ server_actions.working_directory.join(&file_action.file)
|
||||
+ " "
|
||||
+ server_actions.working_directory.join(&new_name),
|
||||
)
|
||||
.run(&logger)
|
||||
.map_err(|e| format!("failed to rename: {e}"))?;
|
||||
}
|
||||
ServerAddress::Localhost => {
|
||||
let dir = &server_actions.working_directory;
|
||||
fs::rename(dir.join(&file_action.file), dir.join(&new_name))
|
||||
.map_err(|e| format!("failed to rename: {e}"))?;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -320,12 +346,25 @@ fn main() -> Result<(), String> {
|
||||
start_ssh_agent(&logger)?;
|
||||
require_non_empty_servers(&servers)?;
|
||||
for server in servers {
|
||||
log!(logger, "Running command on '{}'...", server.ssh_name);
|
||||
ShellCmd::new("ssh")
|
||||
.arg(server.ssh_name)
|
||||
.arg(osf!("cd ") + server.server_directory_path + "; " + &command)
|
||||
.run(&logger)
|
||||
.map_err(|e| format!("{e}"))?;
|
||||
log!(logger, "Running command on '{}'...", server.get_name());
|
||||
match &server.address {
|
||||
ServerAddress::Ssh { ssh_address } => {
|
||||
ShellCmd::new("ssh")
|
||||
.arg(ssh_address)
|
||||
.arg(osf!("cd ") + server.server_directory_path + "; " + &command)
|
||||
.run(&logger)
|
||||
.map_err(|e| format!("{e}"))?;
|
||||
}
|
||||
ServerAddress::Localhost => {
|
||||
let mut command_args =
|
||||
shell_words::split(&command).map_err(|e| format!("failed to parse command: {e}"))?;
|
||||
ShellCmd::new(command_args.remove(0))
|
||||
.args(&command_args)
|
||||
.current_dir(&server.server_directory_path)
|
||||
.run(&logger)
|
||||
.map_err(|e| format!("{e}"))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
log!(logger, "Done!");
|
||||
}
|
||||
@ -339,12 +378,8 @@ fn main() -> Result<(), String> {
|
||||
let download_directory = match download_directory {
|
||||
Some(download_directory) => download_directory,
|
||||
None => {
|
||||
let home_dir = homedir::my_home()
|
||||
.map_err(|e| format!("Failed to determine your home directory: {e}"))
|
||||
.and_then(|home_dir| {
|
||||
home_dir.ok_or("Failed to determine your home directory".to_string())
|
||||
})
|
||||
.map_err(|e| format!("Can't determine download directory: {e}"))?;
|
||||
let home_dir =
|
||||
get_home_directory().map_err(|e| format!("Can't determine download directory: {e}"))?;
|
||||
home_dir.join("Downloads")
|
||||
}
|
||||
};
|
||||
@ -355,7 +390,7 @@ fn main() -> Result<(), String> {
|
||||
fs::create_dir_all(&download_directory)
|
||||
.map_err(|e| format!("failed to create working directory: {e}"))?;
|
||||
|
||||
//make sure file doesn't exist in working directory yet, or will be overridden
|
||||
//make sure file doesn't exist in working directory yet, or it will be overridden
|
||||
let file_name = file
|
||||
.file_name()
|
||||
.ok_or("can only edit files, not directories")?;
|
||||
@ -394,15 +429,19 @@ fn main() -> Result<(), String> {
|
||||
start_ssh_agent(&logger)?;
|
||||
|
||||
for server in servers {
|
||||
log!(logger, "Downloading file from {}...", server.ssh_name);
|
||||
log!(logger, "Getting file from {}...", server.get_name());
|
||||
let file_source = osf!(match &server.address {
|
||||
ServerAddress::Ssh { ssh_address } => format!("{ssh_address}:"),
|
||||
ServerAddress::Localhost => "".to_string(),
|
||||
}) + server.server_directory_path.join(&file);
|
||||
ShellCmd::new("scp")
|
||||
.arg(osf!(&server.ssh_name) + ":" + server.server_directory_path.join(&file))
|
||||
.arg(&file_source)
|
||||
.arg(&download_directory)
|
||||
.run(&logger)
|
||||
.map_err(|e| format!("download failure: {e}"))?;
|
||||
|
||||
//open file in editor
|
||||
let mut shell_args = shell_words::split(&editor)
|
||||
let mut editor_command_args = shell_words::split(&editor)
|
||||
.map_err(|e| format!("failed to parse editor command: {e}"))?
|
||||
.into_iter()
|
||||
.map(|part| {
|
||||
@ -410,16 +449,16 @@ fn main() -> Result<(), String> {
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let command = shell_args.remove(0);
|
||||
let command = editor_command_args.remove(0);
|
||||
ShellCmd::new(command)
|
||||
.args(shell_args)
|
||||
.args(editor_command_args)
|
||||
.run(&logger)
|
||||
.map_err(|e| format!("failed to open file in editor: {e}"))?;
|
||||
|
||||
//upload file again
|
||||
ShellCmd::new("scp")
|
||||
.arg(download_directory.join(file_name))
|
||||
.arg(osf!(&server.ssh_name) + ":" + server.server_directory_path.join(&file))
|
||||
.arg(&file_source)
|
||||
.run(&logger)
|
||||
.map_err(|e| format!("failed to re-upload file: {e}"))?;
|
||||
}
|
||||
@ -431,6 +470,12 @@ fn main() -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_home_directory() -> Result<PathBuf, String> {
|
||||
homedir::my_home()
|
||||
.map_err(|e| format!("Failed to determine home directory: {e}"))
|
||||
.and_then(|home_dir| home_dir.ok_or("Failed to find home directory".to_string()))
|
||||
}
|
||||
|
||||
fn require_non_empty_servers(servers: &[Server]) -> Result<(), String> {
|
||||
if servers.is_empty() {
|
||||
Err("You did not provide any servers for this operation. Please see --help".to_string())
|
||||
@ -483,28 +528,32 @@ fn parse_server_configuration(config_str: &str) -> Result<Vec<Server>, String> {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::parse_server_configuration;
|
||||
use crate::server::Server;
|
||||
use crate::server::{Server, ServerAddress};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[test]
|
||||
fn test_parse_server_configuration() {
|
||||
let servers =
|
||||
parse_server_configuration("foo:bar,fizz:buzz/bizz").expect("valid server configuration");
|
||||
parse_server_configuration("foo:bar,.:fizz/buzz").expect("valid server configuration");
|
||||
assert_eq!(
|
||||
vec![
|
||||
Server {
|
||||
ssh_name: "foo".to_string(),
|
||||
address: ServerAddress::Ssh {
|
||||
ssh_address: "foo".to_string()
|
||||
},
|
||||
server_directory_path: PathBuf::from("bar"),
|
||||
},
|
||||
Server {
|
||||
ssh_name: "fizz".to_string(),
|
||||
server_directory_path: PathBuf::from("buzz/bizz"),
|
||||
address: ServerAddress::Localhost,
|
||||
server_directory_path: PathBuf::from("fizz/buzz"),
|
||||
}
|
||||
],
|
||||
servers
|
||||
);
|
||||
}
|
||||
|
||||
/// When we join an absolute path to a relative path, it becomes a relative path
|
||||
#[test]
|
||||
fn path_experiment() {
|
||||
let server_dir = PathBuf::from("steptech");
|
||||
@ -513,4 +562,12 @@ mod test {
|
||||
let joined = server_dir.join(upload_dir);
|
||||
assert_eq!(PathBuf::from("/home"), joined);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_experiment() {
|
||||
fs::rename("test-ressources/files/test", "test-ressources/files/test1")
|
||||
.expect("failed to rename test file");
|
||||
fs::rename("test-ressources/files/test1", "test-ressources/files/test")
|
||||
.expect("failed to rename test1 file back to test");
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,6 +98,9 @@ macro_rules! osf {
|
||||
use $crate::os_string_builder::OsStringBuilder;
|
||||
OsStringBuilder::default()
|
||||
}};
|
||||
($s:literal $(,$arg:tt)*) => {
|
||||
osf!() + format!($s, $($arg)*)
|
||||
};
|
||||
($s:expr) => {
|
||||
osf!() + $s
|
||||
};
|
||||
@ -110,6 +113,10 @@ mod test_builder {
|
||||
#[test]
|
||||
fn test_build() {
|
||||
assert_eq!(osf!("foo") + "Bar", "fooBar");
|
||||
assert_eq!(osf!(PathBuf::from("foo")) + "Bar", "fooBar");
|
||||
let o = 'o';
|
||||
assert_eq!(osf!("fo{o}") + "Bar", "fooBar");
|
||||
assert_eq!(osf!("fo{}", o) + "Bar", "fooBar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
113
src/server.rs
113
src/server.rs
@ -1,3 +1,4 @@
|
||||
use crate::get_home_directory;
|
||||
use std::cell::LazyCell;
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
@ -9,14 +10,14 @@ use std::str::FromStr;
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ServerReference {
|
||||
Resolved(Server),
|
||||
Name(String),
|
||||
Identifier(String),
|
||||
}
|
||||
|
||||
impl ServerReference {
|
||||
pub fn get_name(&self) -> &str {
|
||||
pub fn get_identifier(&self) -> &str {
|
||||
match self {
|
||||
ServerReference::Resolved(server) => &server.ssh_name,
|
||||
ServerReference::Name(name) => name,
|
||||
ServerReference::Resolved(server) => server.address.identifier(),
|
||||
ServerReference::Identifier(id) => id,
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,7 +25,7 @@ impl ServerReference {
|
||||
pub fn resolve(self, configured_servers: &[Server]) -> Option<Server> {
|
||||
match self {
|
||||
ServerReference::Resolved(server) => Some(server),
|
||||
ServerReference::Name(name) => Self::resolve_server_name(&name, configured_servers),
|
||||
ServerReference::Identifier(name) => Self::resolve_server_name(&name, configured_servers),
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,7 +37,7 @@ impl ServerReference {
|
||||
{
|
||||
match self {
|
||||
ServerReference::Resolved(server) => Some(server),
|
||||
ServerReference::Name(name) => Self::resolve_server_name(&name, provider),
|
||||
ServerReference::Identifier(name) => Self::resolve_server_name(&name, provider),
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,17 +52,17 @@ impl ServerReference {
|
||||
{
|
||||
match self {
|
||||
ServerReference::Resolved(server) => Ok(Some(server)),
|
||||
ServerReference::Name(name) => provider
|
||||
ServerReference::Identifier(name) => provider
|
||||
.as_ref()
|
||||
.map_err(|e| e.clone())
|
||||
.map(|servers| Self::resolve_server_name(&name, servers)),
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_server_name(name: &str, servers: &[Server]) -> Option<Server> {
|
||||
fn resolve_server_name(identifier: &str, servers: &[Server]) -> Option<Server> {
|
||||
servers
|
||||
.iter()
|
||||
.find(|server| server.ssh_name == name)
|
||||
.find(|server| server.address.identifier() == identifier)
|
||||
.cloned()
|
||||
}
|
||||
}
|
||||
@ -72,13 +73,13 @@ impl FromStr for ServerReference {
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Server::from_str(s)
|
||||
.map(Self::Resolved)
|
||||
.or_else(|_| Ok(Self::Name(s.to_string())))
|
||||
.or_else(|_| Ok(Self::Identifier(s.to_string())))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ServerReference {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.get_name() == other.get_name()
|
||||
self.get_identifier() == other.get_identifier()
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,7 +87,7 @@ impl Eq for ServerReference {}
|
||||
|
||||
impl Hash for ServerReference {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.get_name().hash(state);
|
||||
self.get_identifier().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,7 +95,7 @@ impl Display for ServerReference {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ServerReference::Resolved(server) => write!(f, "{}", server),
|
||||
ServerReference::Name(name) => write!(f, "{}", name),
|
||||
ServerReference::Identifier(name) => write!(f, "{}", name),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -112,32 +113,95 @@ impl Error for ServerReferenceParseError {}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Server {
|
||||
pub ssh_name: String,
|
||||
pub address: ServerAddress,
|
||||
pub server_directory_path: PathBuf,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn get_name(&self) -> &str {
|
||||
match &self.address {
|
||||
ServerAddress::Ssh { ssh_address } => ssh_address,
|
||||
ServerAddress::Localhost => "this computer",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Server {
|
||||
type Err = ServerParseError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
s.split_once(':')
|
||||
.ok_or(ServerParseError::MissingServerDirectory)
|
||||
.map(|(name, directory)| Self {
|
||||
ssh_name: name.to_string(),
|
||||
server_directory_path: PathBuf::from(directory),
|
||||
.and_then(|(identifier, server_directory)| {
|
||||
let address = ServerAddress::from_str(identifier);
|
||||
let mut server_directory_path = PathBuf::from(server_directory);
|
||||
if let ServerAddress::Localhost = &address {
|
||||
let home_directory = get_home_directory()
|
||||
.map_err(|e| ServerParseError::HomeDirectoryRequired { detail_message: e })?;
|
||||
server_directory_path = home_directory.join(&server_directory_path);
|
||||
}
|
||||
Ok(Self {
|
||||
address,
|
||||
server_directory_path,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Server {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}:{}", self.ssh_name, self.server_directory_path.to_string_lossy())
|
||||
write!(
|
||||
f,
|
||||
"{}{}",
|
||||
match &self.address {
|
||||
ServerAddress::Ssh { ssh_address } => format!("{ssh_address}:"),
|
||||
ServerAddress::Localhost => "".to_string(),
|
||||
},
|
||||
self.server_directory_path.to_string_lossy()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum ServerAddress {
|
||||
Ssh { ssh_address: String },
|
||||
Localhost,
|
||||
}
|
||||
|
||||
impl ServerAddress {
|
||||
pub fn ssh<S>(ssh_address: S) -> Self
|
||||
where
|
||||
S: ToString,
|
||||
{
|
||||
Self::Ssh {
|
||||
ssh_address: ssh_address.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str<S>(s: S) -> Self
|
||||
where
|
||||
S: ToString,
|
||||
{
|
||||
let s = s.to_string();
|
||||
if s == "." {
|
||||
Self::Localhost
|
||||
} else {
|
||||
Self::ssh(s)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn identifier(&self) -> &str {
|
||||
match self {
|
||||
ServerAddress::Ssh { ssh_address } => ssh_address,
|
||||
ServerAddress::Localhost => ".",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ServerParseError {
|
||||
MissingServerDirectory,
|
||||
HomeDirectoryRequired { detail_message: String },
|
||||
}
|
||||
|
||||
impl Display for ServerParseError {
|
||||
@ -150,6 +214,10 @@ impl Display for ServerParseError {
|
||||
double colon to point to the home directory, e.g: 'lobby:'"
|
||||
)
|
||||
}
|
||||
ServerParseError::HomeDirectoryRequired { detail_message } => write!(
|
||||
f,
|
||||
"localhost requires home directory, but: {detail_message}"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -158,23 +226,24 @@ impl Error for ServerParseError {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_server_reference {
|
||||
use crate::server::{Server, ServerReference};
|
||||
use crate::server::{Server, ServerAddress, ServerReference};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn test_from_str() {
|
||||
assert_eq!(
|
||||
ServerReference::Name("foo".to_string()),
|
||||
ServerReference::Identifier("foo".to_string()),
|
||||
ServerReference::from_str("foo").unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
ServerReference::Resolved(Server {
|
||||
ssh_name: "crea".to_string(),
|
||||
address: ServerAddress::Ssh {
|
||||
ssh_address: "crea".to_string()
|
||||
},
|
||||
server_directory_path: PathBuf::from("server/creative2")
|
||||
}),
|
||||
ServerReference::from_str("crea:server/creative2").unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user