120 lines
3.7 KiB
Rust
120 lines
3.7 KiB
Rust
mod backup;
|
|
mod config;
|
|
mod run;
|
|
|
|
use std::{path::PathBuf, str::FromStr};
|
|
|
|
use clap::{Args, Parser, Subcommand};
|
|
use figment::{
|
|
providers::{Env, Format, Serialized, Toml},
|
|
Figment,
|
|
};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::{backup::ManagerConfig, server::ServerType};
|
|
use backup::BackupArgs;
|
|
use config::Config;
|
|
use run::RunCli;
|
|
|
|
#[derive(Parser, Serialize)]
|
|
#[command(author, version, about, long_about = None)]
|
|
pub struct Cli {
|
|
#[command(subcommand)]
|
|
#[serde(skip)]
|
|
pub command: Commands,
|
|
|
|
/// Path to a TOML configuration file
|
|
#[arg(long = "config-file", global = true)]
|
|
pub config_file: Option<PathBuf>,
|
|
|
|
#[command(flatten)]
|
|
pub args: CliArgs,
|
|
}
|
|
|
|
#[derive(Args, Serialize, Deserialize, Clone)]
|
|
pub struct CliArgs {
|
|
/// Directory where configs are stored, and where the server will run
|
|
#[arg(long, value_name = "CONFIG_DIR", global = true)]
|
|
#[serde(skip_serializing_if = "::std::option::Option::is_none")]
|
|
pub config: Option<PathBuf>,
|
|
|
|
/// Directory where world files will be saved
|
|
#[arg(long, value_name = "WORLD_DIR", global = true)]
|
|
#[serde(skip_serializing_if = "::std::option::Option::is_none")]
|
|
pub world: Option<PathBuf>,
|
|
|
|
/// Directory where backups will be stored
|
|
#[arg(long, value_name = "BACKUP_DIR", global = true)]
|
|
#[serde(skip_serializing_if = "::std::option::Option::is_none")]
|
|
pub backup: Option<PathBuf>,
|
|
|
|
/// What backup layers to employ, provided as a list of tuples name,frequency,chains,chain_len
|
|
/// delimited by semicolons (;).
|
|
#[arg(long, global = true, value_delimiter = ';')]
|
|
#[serde(skip_serializing_if = "::std::option::Option::is_none")]
|
|
pub layers: Option<Vec<ManagerConfig>>,
|
|
|
|
/// Type of server
|
|
#[arg(long, global = true)]
|
|
#[serde(skip_serializing_if = "::std::option::Option::is_none")]
|
|
pub server: Option<ServerType>,
|
|
|
|
/// Version string for the server, e.g. 1.19.4-545
|
|
#[arg(long, global = true)]
|
|
#[serde(skip_serializing_if = "::std::option::Option::is_none")]
|
|
pub server_version: Option<String>,
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
pub enum Commands {
|
|
/// Run the server
|
|
Run(RunCli),
|
|
/// Interact with the backup system without starting a server
|
|
Backup(BackupArgs),
|
|
}
|
|
|
|
impl Cli {
|
|
pub fn run(&self) -> crate::Result<()> {
|
|
let config = self.config(&self.args)?;
|
|
|
|
match &self.command {
|
|
Commands::Run(args) => args.run(self, &config),
|
|
Commands::Backup(args) => Ok(args.run(&config)?),
|
|
}
|
|
}
|
|
|
|
pub fn config<T, U>(&self, args: &U) -> crate::Result<T>
|
|
where
|
|
T: Default + Serialize + for<'de> Deserialize<'de>,
|
|
U: Serialize,
|
|
{
|
|
let toml_file = self
|
|
.config_file
|
|
.clone()
|
|
.unwrap_or(PathBuf::from(Env::var_or("ALEX_CONFIG_FILE", "")));
|
|
|
|
let mut figment = Figment::new()
|
|
.merge(Serialized::defaults(T::default()))
|
|
.merge(Toml::file(toml_file))
|
|
.merge(Env::prefixed("ALEX_").ignore(&["ALEX_LAYERS"]));
|
|
|
|
// Layers need to be parsed separately, as the env var format is different than the one
|
|
// serde expects
|
|
if let Some(layers_env) = Env::var("ALEX_LAYERS") {
|
|
let res = layers_env
|
|
.split(';')
|
|
.map(ManagerConfig::from_str)
|
|
.collect::<Vec<_>>();
|
|
|
|
if res.iter().any(|e| e.is_err()) {
|
|
return Err(crate::other("Invalid layer configuration").into());
|
|
}
|
|
|
|
let layers: Vec<_> = res.iter().flatten().collect();
|
|
figment = figment.merge(Serialized::default("layers", layers));
|
|
}
|
|
|
|
Ok(figment.merge(Serialized::defaults(args)).extract()?)
|
|
}
|
|
}
|