use clap::{Parser, Subcommand}; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] pub struct Args { #[command(subcommand)] pub command: Option, #[arg(short, long, global = true, default_value_t = 8080, help = "Port to listen on")] pub port: u16, #[arg(long, default_value = "./", help = "Path to store database files")] pub path: String, #[arg(long, default_value_t = false, help = "Serve gree headers with https. WILL NOT ACCEPT HTTPS REQUESTS")] pub https: bool, #[arg(long, default_value = "http://127.0.0.1:51376", help = "Address to NPPS4 server for sif account linking")] pub npps4: String, //below options are for the "Help" page #[arg(long, default_value = "", help = "Link to patched android global apk for this server.")] pub global_android: String, #[arg(long, default_value = "", help = "Link to patched android japan apk for this server.")] pub japan_android: String, #[arg(long, default_value = "", help = "Link to patched iOS global apk for this server.")] pub global_ios: String, #[arg(long, default_value = "", help = "Link to patched iOS japan apk for this server.")] pub japan_ios: String, #[arg(long, default_value = "", help = "Link to asset server.")] pub assets_url: String, #[arg(long, default_value_t = 0, help = "Max time returned by the server, in the JSON \"timestamp\" key.")] pub max_time: u64, #[arg(long, default_value_t = false, help = "Disable webui, act completely like the original server")] pub hidden: bool, #[arg(long, default_value_t = false, help = "Enable the custom songs feature (upload/browse/download). Disabled by default; every custom-songs endpoint and webui element is hidden unless this is set")] pub enable_custom_songs: bool, #[arg(long, default_value_t = false, help = "Enable the custom cards feature (upload/manage runtime cards and characters). Disabled by default; every custom-cards endpoint and webui element is hidden unless this is set")] pub enable_custom_cards: bool, #[arg(long, default_value_t = false, help = "Nerf custom cards: cap base stats below the official per-rarity maxima (median band) and skill values at 80% of the official range. Applies at upload time and to cards already uploaded, which are clamped in every response")] pub nerf_custom_cards: bool, #[arg(long, default_value_t = false, help = "Enable the custom 3D MV feature (upload/manage MMD model+motion MVs for custom songs). Disabled by default; every custom-3dmv endpoint and webui element is hidden unless this is set")] pub enable_custom_3dmv: bool, #[arg(long, default_value_t = false, help = "Enable arcade mode (cabinet registration, per-machine guest accounts, LP-free arcade lives). Disabled by default; every /api/arcade endpoint and webui element is hidden unless this is set")] pub enable_arcade: bool, #[arg(long, default_value_t = 90, help = "Days an arcade machine may go unseen before --purge deletes it together with its machine and guest accounts")] pub arcade_machine_ttl: u64, #[arg(long, default_value_t = 30, help = "Minutes a card-bound account may play LP-free after the cabinet ran /api/arcade/session for its card. Each arcade live restarts the window, up to four windows from the session itself; outside it the account is an ordinary phone account and pays LP")] pub arcade_session_ttl: u64, #[arg(long, value_delimiter = ',', help = "User id(s) of the server owner(s), repeatable or comma separated. Owner accounts implicitly hold every permission scope and are the only ones able to grant scopes on a fresh install")] pub owner: Vec, #[arg(long, default_value_t = false, help = "Purge dead user accounts on startup")] pub purge: bool, #[arg(long, default_value_t = false, help = "Disable user account imports")] pub disable_imports: bool, #[arg(long, default_value_t = false, help = "Disable user account exports")] pub disable_exports: bool, #[arg(long, default_value = "", help = "Asset hash for English iOS client.")] pub en_ios_asset_hash: String, #[arg(long, default_value = "", help = "Asset hash for JP iOS client.")] pub jp_ios_asset_hash: String, #[arg(long, default_value = "", help = "Asset hash for English Android client.")] pub en_android_asset_hash: String, #[arg(long, default_value = "", help = "Asset hash for JP Android client.")] pub jp_android_asset_hash: String, #[arg(long, default_value = "", help = "Asset version for overridden asset hashes.")] pub asset_version: String, #[arg(long, default_value = "", help = "Global asset version for overridden asset hashes.")] pub en_asset_version: String, #[arg(long, default_value_t = false, help = "Force an application update for asset versions not marked as latest")] pub force_updates: bool, #[arg(long, default_value = "", help = "Asset hash for windows client.")] pub windows_asset_hash: String, #[arg(long, default_value = "", help = "Asset hash for linux client.")] pub linux_asset_hash: String, #[arg(long, default_value = "", help = "Asset hash for macOS client.")] pub macos_asset_hash: String, #[arg(long, default_value = "", help = "Path to image assets.")] pub image_asset_path: String, #[arg(long, default_value = "", help = "Optional directory to load master data CSVs from at runtime. Layout mirrors the bundled assets (csv/, csv-en/). Missing files fall back to the internal copies.")] pub masterdata: String } #[derive(Subcommand, Debug)] pub enum Commands { /// Start a maintenance-only server until stopped Maintenance { /// The message shown to the client message: String, }, } pub fn get_args() -> Args { #[cfg(not(test))] let mut args = Args::parse(); #[cfg(test)] let mut args = Args::parse_from(["ew"]); crate::runtime::overlay_args(&mut args); args } #[cfg(test)] mod tests { use super::*; #[test] fn maintenance_command_and_port() { for argv in [ vec!["ew", "--port", "6017", "maintenance", "Updating songs"], vec!["ew", "maintenance", "Updating songs", "--port", "6017"], ] { let args = Args::try_parse_from(argv).unwrap(); assert_eq!(args.port, 6017); assert!(matches!(args.command, Some(Commands::Maintenance { message }) if message == "Updating songs")); } assert!(Args::try_parse_from(["ew", "maintenance"]).is_err()); let normal = Args::try_parse_from(["ew"]).unwrap(); assert!(normal.command.is_none()); assert_eq!(normal.port, 8080); } }