diff --git a/src/cli/backup.rs b/src/cli/backup.rs new file mode 100644 index 0000000..2eeb4e4 --- /dev/null +++ b/src/cli/backup.rs @@ -0,0 +1,12 @@ +use clap::{Args, Subcommand}; + +#[derive(Subcommand)] +pub enum BackupCommands { + List, +} + +#[derive(Args)] +pub struct BackupArgs { + #[command(subcommand)] + pub command: BackupCommands, +} diff --git a/src/cli.rs b/src/cli/mod.rs similarity index 59% rename from src/cli.rs rename to src/cli/mod.rs index 8d44af3..9ecd9f3 100644 --- a/src/cli.rs +++ b/src/cli/mod.rs @@ -1,8 +1,23 @@ +mod backup; +mod run; + +pub use backup::{BackupArgs, BackupCommands}; +pub use run::RunArgs; + use crate::backup::ManagerConfig; use crate::server::ServerType; -use clap::{Args, Parser, Subcommand}; +use clap::{Parser, Subcommand}; +use std::io; use std::path::PathBuf; +#[derive(Subcommand)] +pub enum Commands { + /// Run the server + Run(RunArgs), + /// Interact with the backup system without starting a server + Backup(BackupArgs), +} + #[derive(Parser)] #[command(author, version, about, long_about = None)] pub struct Cli { @@ -36,7 +51,7 @@ pub struct Cli { )] pub backup: PathBuf, - /// What backup layers to employ, provided as a list of tuples name,frequency,chain_len,chains + /// What backup layers to employ, provided as a list of tuples name,frequency,chains,chain_len /// delimited by semicolons (;). #[arg(long, env = "ALEX_LAYERS", global = true, value_delimiter = ';')] pub layers: Vec, @@ -49,42 +64,11 @@ pub struct Cli { pub server_version: String, } -#[derive(Subcommand)] -pub enum Commands { - /// Run the server - Run(RunArgs), - /// Create a new backup of the server. This command should only be used when the server is not - /// running. - Backup(BackupArgs), +impl Cli { + pub fn run(&self) -> io::Result<()> { + match &self.command { + Commands::Run(args) => args.run(self), + Commands::Backup(_) => Ok(()), + } + } } - -#[derive(Args)] -pub struct RunArgs { - /// Server jar to execute - #[arg( - long, - value_name = "JAR_PATH", - default_value = "server.jar", - env = "ALEX_JAR" - )] - pub jar: PathBuf, - - /// Java command to run the server jar with - #[arg(long, value_name = "JAVA_CMD", default_value_t = String::from("java"), env = "ALEX_JAVA")] - pub java: String, - - /// XMS value in megabytes for the server instance - #[arg(long, default_value_t = 1024, env = "ALEX_XMS")] - pub xms: u64, - /// XMX value in megabytes for the server instance - #[arg(long, default_value_t = 2048, env = "ALEX_XMX")] - pub xmx: u64, - - /// Don't actually run the server, but simply output the server configuration that would have - /// been ran - #[arg(short, long, default_value_t = false)] - pub dry: bool, -} - -#[derive(Args)] -pub struct BackupArgs {} diff --git a/src/cli/run.rs b/src/cli/run.rs new file mode 100644 index 0000000..986fe8a --- /dev/null +++ b/src/cli/run.rs @@ -0,0 +1,94 @@ +use crate::cli::Cli; +use crate::server; +use crate::signals; +use crate::stdin; +use clap::Args; +use std::io; +use std::path::PathBuf; +use std::sync::{Arc, Mutex}; + +#[derive(Args)] +pub struct RunArgs { + /// Server jar to execute + #[arg( + long, + value_name = "JAR_PATH", + default_value = "server.jar", + env = "ALEX_JAR" + )] + pub jar: PathBuf, + + /// Java command to run the server jar with + #[arg(long, value_name = "JAVA_CMD", default_value_t = String::from("java"), env = "ALEX_JAVA")] + pub java: String, + + /// XMS value in megabytes for the server instance + #[arg(long, default_value_t = 1024, env = "ALEX_XMS")] + pub xms: u64, + /// XMX value in megabytes for the server instance + #[arg(long, default_value_t = 2048, env = "ALEX_XMX")] + pub xmx: u64, + + /// Don't actually run the server, but simply output the server configuration that would have + /// been ran + #[arg(short, long, default_value_t = false)] + pub dry: bool, +} + +fn backups_thread(counter: Arc>) { + loop { + let next_scheduled_time = { + let server = counter.lock().unwrap(); + server.backups.next_scheduled_time().unwrap() + }; + + let now = chrono::offset::Utc::now(); + if next_scheduled_time > now { + std::thread::sleep((next_scheduled_time - now).to_std().unwrap()); + } + + { + let mut server = counter.lock().unwrap(); + + // We explicitely ignore the error here, as we don't want the thread to fail + let _ = server.backup(); + } + } +} + +impl RunArgs { + pub fn run(&self, cli: &Cli) -> io::Result<()> { + let (_, mut signals) = signals::install_signal_handlers()?; + + let mut cmd = server::ServerCommand::new(cli.server, &cli.server_version) + .java(&self.java) + .jar(self.jar.clone()) + .config(cli.config.clone()) + .world(cli.world.clone()) + .backup(cli.backup.clone()) + .managers(cli.layers.clone()) + .xms(self.xms) + .xmx(self.xmx); + cmd.canonicalize()?; + + if self.dry { + print!("{}", cmd); + + return Ok(()); + } + + let counter = Arc::new(Mutex::new(cmd.spawn()?)); + + if !cli.layers.is_empty() { + let clone = Arc::clone(&counter); + std::thread::spawn(move || backups_thread(clone)); + } + + // Spawn thread that handles the main stdin loop + let clone = Arc::clone(&counter); + std::thread::spawn(move || stdin::handle_stdin(clone)); + + // Signal handler loop exits the process when necessary + signals::handle_signals(&mut signals, counter) + } +} diff --git a/src/main.rs b/src/main.rs index b7187e4..edad9c3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,84 +4,31 @@ mod server; mod signals; mod stdin; +use crate::cli::Cli; use clap::Parser; -use cli::{Cli, Commands, RunArgs}; use std::io; -use std::sync::{Arc, Mutex}; -fn backups_thread(counter: Arc>) { - loop { - let next_scheduled_time = { - let server = counter.lock().unwrap(); - server.backups.next_scheduled_time().unwrap() - }; - - let now = chrono::offset::Utc::now(); - if next_scheduled_time > now { - std::thread::sleep((next_scheduled_time - now).to_std().unwrap()); - } - - { - let mut server = counter.lock().unwrap(); - - // We explicitely ignore the error here, as we don't want the thread to fail - let _ = server.backup(); - } - } -} - -fn command_run(cli: &Cli, args: &RunArgs) -> io::Result<()> { - let (_, mut signals) = signals::install_signal_handlers()?; - - let mut cmd = server::ServerCommand::new(cli.server, &cli.server_version) - .java(&args.java) - .jar(args.jar.clone()) - .config(cli.config.clone()) - .world(cli.world.clone()) - .backup(cli.backup.clone()) - .managers(cli.layers.clone()) - .xms(args.xms) - .xmx(args.xmx); - cmd.canonicalize()?; - - if args.dry { - print!("{}", cmd); - - return Ok(()); - } - - let counter = Arc::new(Mutex::new(cmd.spawn()?)); - - if !cli.layers.is_empty() { - let clone = Arc::clone(&counter); - std::thread::spawn(move || backups_thread(clone)); - } - - // Spawn thread that handles the main stdin loop - let clone = Arc::clone(&counter); - std::thread::spawn(move || stdin::handle_stdin(clone)); - - // Signal handler loop exits the process when necessary - signals::handle_signals(&mut signals, counter) -} - -// fn commands_backup(cli: &Cli, _args: &BackupArgs) -> io::Result<()> { +// fn commands_backup(cli: &Cli, args: &BackupArgs) -> io::Result<()> { // let metadata = server::Metadata { // server_type: cli.server, // server_version: cli.server_version.clone(), // }; -// let mut meta = MetaManager::new(cli.backup, cli.config, cli.world, metadata); +// let dirs = vec![ +// (PathBuf::from("config"), cli.config.clone()), +// (PathBuf::from("worlds"), cli.world.clone()), +// ]; +// let mut meta = MetaManager::new(cli.backup.clone(), dirs, metadata); // meta.add_all(&cli.layers)?; -// manager.create_backup()?; -// manager.remove_old_backups() +// match &args.command { +// BackupCommands::List => () +// } + +// // manager.create_backup()?; +// // manager.remove_old_backups() // } fn main() -> io::Result<()> { let cli = Cli::parse(); - - match &cli.command { - Commands::Run(args) => command_run(&cli, args), - Commands::Backup(_) => Ok(()), - } + cli.run() }