mod cli; mod config; mod config_diff; mod daemon; mod deploy; mod rclone; mod scheduler; mod services; mod supervisor; mod web; use std::path::PathBuf; use anyhow::Result; use clap::{Parser, Subcommand}; use config::Config; /// Warpgate — Make your NAS feel local. /// /// SSD read-write caching proxy for remote NAS access. #[derive(Parser)] #[command(name = "warpgate", version, about)] struct Cli { /// Path to config file. #[arg(short, long, default_value = config::DEFAULT_CONFIG_PATH)] config: PathBuf, #[command(subcommand)] command: Commands, } #[derive(Subcommand)] enum Commands { /// One-click deploy: install deps, generate configs, enable services. Deploy, /// Show service status, cache stats, write-back queue, bandwidth. Status, /// List files currently in the SSD cache. CacheList, /// Clean cached files (only evicts clean files, never dirty). CacheClean { /// Remove all clean files (default: only expired). #[arg(long)] all: bool, }, /// Pre-cache a remote directory to local SSD. Warmup { /// Name of the share to warm up. #[arg(long)] share: String, /// Path within the share to warm up. path: String, /// Only files newer than this duration (e.g. "7d", "24h"). #[arg(long)] newer_than: Option, }, /// View or adjust bandwidth limits at runtime. Bwlimit { /// Upload limit (e.g. "10M", "0" for unlimited). #[arg(long)] up: Option, /// Download limit (e.g. "50M", "0" for unlimited). #[arg(long)] down: Option, }, /// Stream service logs in real time. Log { /// Number of recent lines to show. #[arg(short, long, default_value = "50")] lines: u32, /// Follow log output (like tail -f). #[arg(short, long)] follow: bool, }, /// Test network speed to remote NAS. SpeedTest, /// Run all services under a single supervisor process. Run, /// Generate a default config file. ConfigInit { /// Output path (default: /etc/warpgate/config.toml). #[arg(short, long)] output: Option, }, } fn main() -> Result<()> { let cli = Cli::parse(); match cli.command { // config-init doesn't need an existing config file Commands::ConfigInit { output } => cli::config_init::run(output), // deploy loads config if it exists, or generates one Commands::Deploy => { let config = load_config_or_default(&cli.config)?; deploy::setup::run(&config) } // all other commands require a valid config cmd => { let config = Config::load(&cli.config)?; match cmd { Commands::Status => cli::status::run(&config), Commands::CacheList => cli::cache::list(&config), Commands::CacheClean { all } => cli::cache::clean(&config, all), Commands::Warmup { share, path, newer_than } => { cli::warmup::run(&config, &share, &path, newer_than.as_deref()) } Commands::Bwlimit { up, down } => { cli::bwlimit::run(&config, up.as_deref(), down.as_deref()) } Commands::Log { lines, follow } => cli::log::run(&config, lines, follow), Commands::SpeedTest => cli::speed_test::run(&config), Commands::Run => supervisor::run(&config, cli.config.clone()), // already handled above Commands::ConfigInit { .. } | Commands::Deploy => unreachable!(), } } } } /// Load config from file, or return a useful error. fn load_config_or_default(path: &std::path::Path) -> Result { if path.exists() { Config::load(path) } else { anyhow::bail!( "Config file not found: {}. Run `warpgate config-init` to generate one.", path.display() ) } }