2024-12-11 14:13:32 +01:00
use clap ::builder ::Str ;
use clap ::{ Parser , Subcommand , ValueEnum } ;
2024-12-11 10:42:44 +01:00
use std ::env ;
use std ::path ::PathBuf ;
2024-12-11 14:13:32 +01:00
const SERVERS_ENV_VAR : & str = " MSSH_SERVERS " ;
2024-12-11 10:42:44 +01:00
2024-12-11 14:13:32 +01:00
/// Uploads a file or executes a command on multiple configured servers
2024-12-11 10:42:44 +01:00
///
2024-12-11 14:13:32 +01:00
/// Servers must either be configured via environment variable or denote their server directory with
/// a double colon: crea:home/crea.
2024-12-11 10:42:44 +01:00
///
2024-12-11 14:13:32 +01:00
/// --- Configuration via environment variable ---
2024-12-11 10:42:44 +01:00
///
2024-12-11 14:13:32 +01:00
/// Use MSSH_SERVERS="crea:home/crea,sky:sky,lobby:,city:city2" to configure servers.
2024-12-11 10:42:44 +01:00
#[ derive(Parser, Debug) ]
#[ command(version, about, long_about) ]
struct Args {
2024-12-11 14:13:32 +01:00
/// The action to perform
#[ command(subcommand) ]
command : Command ,
/// The ssh names and optionally home directories of the servers to perform the action on
2024-12-11 10:42:44 +01:00
#[ arg(num_args = 1..) ]
2024-12-11 14:13:32 +01:00
servers : Vec < String > , //TODO directly parse servers and their directories
}
#[ derive(Subcommand, Debug) ]
enum Command {
/// Upload a file to the servers
#[ command(visible_short_flag_alias = 'u') ]
Upload {
/// The file to upload
file : PathBuf ,
/// How to handle older versions of the file
#[ arg(short = 'a', long, default_value = " delete " , default_missing_value = " archive " , num_args = 0..1) ]
old_version_policy : OldVersionPolicy ,
} ,
/// Execute a command on the servers
#[ command(visible_short_flag_alias = 'c') ]
Command {
/// The command to execute
command : String ,
} ,
}
#[ derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default, ValueEnum) ]
enum OldVersionPolicy {
/// Ignore the existence of older versions
Ignore ,
/// Rename older versions: foo.jar -> foo.jarr
Archive ,
/// Delete older versions
#[ default ]
Delete ,
2024-12-11 10:42:44 +01:00
}
#[ derive(Debug, Clone, Eq, PartialEq, Hash) ]
struct Server {
pub ssh_name : String ,
pub server_directory_path : PathBuf ,
}
#[ derive(Debug, Clone, Eq, PartialEq, Hash) ]
struct PluginInfo {
pub name : String ,
pub version : Option < String > ,
}
fn main ( ) -> Result < ( ) , String > {
let args = Args ::parse ( ) ;
dbg! ( & args ) ;
let configured_servers = parse_server_configuration_from_env ( ) ? ;
dbg! ( & configured_servers ) ;
2024-12-11 14:13:32 +01:00
2024-12-11 10:42:44 +01:00
let servers = args
. servers
. iter ( )
. map ( | server_name | {
configured_servers
. iter ( )
. find ( | server | server . ssh_name = = * server_name )
2024-12-11 14:13:32 +01:00
. ok_or ( format! (
" no server with the name '{server_name}' has been configured "
) )
2024-12-11 10:42:44 +01:00
} )
. collect ::< Result < Vec < _ > , _ > > ( ) ? ;
Ok ( ( ) )
}
fn parse_server_configuration_from_env ( ) -> Result < Vec < Server > , String > {
env ::var ( SERVERS_ENV_VAR )
. map_err ( | _ | format! ( " Missing environment variable {} " , SERVERS_ENV_VAR ) )
. and_then ( | value | parse_server_configuration ( & value ) )
}
fn parse_server_configuration ( config_str : & str ) -> Result < Vec < Server > , 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 ) ,
} )
} ) . collect ( )
}
#[ cfg(test) ]
mod test {
use crate ::{ parse_server_configuration , Server } ;
use std ::path ::PathBuf ;
#[ test ]
fn test_parse_server_configuration ( ) {
2024-12-11 14:13:32 +01:00
let servers =
parse_server_configuration ( " foo:bar,fizz:buzz/bizz " ) . expect ( " valid server configuration " ) ;
assert_eq! (
vec! [
Server {
ssh_name : " foo " . to_string ( ) ,
server_directory_path : PathBuf ::from ( " bar " ) ,
} ,
Server {
ssh_name : " fizz " . to_string ( ) ,
server_directory_path : PathBuf ::from ( " buzz/bizz " ) ,
}
] ,
servers
) ;
2024-12-11 10:42:44 +01:00
}
}