use anyhow::Context; use clap::Parser; use directories_next::ProjectDirs; use log::debug; use std::path::PathBuf; use vmadm::cmd; use vmadm::config::Configuration; use vmadm::progress::{MessageKind, Progress}; use vmadm::spec::Specification; const QUALIFIER: &str = ""; const ORG: &str = ""; const APP: &str = "vmadm"; #[derive(Debug, Parser)] struct Cli { #[clap(subcommand)] cmd: Command, } #[derive(Debug, Parser)] enum Command { #[clap(visible_alias = "create")] New { #[clap(flatten)] common: CommonOptions, specs: Vec, }, Recreate { #[clap(flatten)] common: CommonOptions, specs: Vec, }, Config { #[clap(flatten)] common: CommonOptions, }, Spec { #[clap(flatten)] common: CommonOptions, spec: PathBuf, }, List { #[clap(flatten)] common: CommonOptions, }, #[clap(visible_alias = "remove")] Delete { #[clap(flatten)] common: CommonOptions, specs: Vec, }, Start { #[clap(flatten)] common: CommonOptions, specs: Vec, }, #[clap(visible_alias = "restart")] Reboot { #[clap(flatten)] common: CommonOptions, specs: Vec, }, #[clap(visible_alias = "stop")] Shutdown { #[clap(flatten)] common: CommonOptions, specs: Vec, }, CloudInit { #[clap(flatten)] common: CommonOptions, spec: PathBuf, dirname: PathBuf, }, } #[derive(Debug, Parser)] struct CommonOptions { #[clap(short, long)] config: Option, #[clap(short, long)] quiet: bool, #[clap(short, long)] verbose: bool, } fn main() -> anyhow::Result<()> { pretty_env_logger::init_custom_env("VMADM_LOG"); let cli = Cli::parse(); debug!("{:#?}", cli); match cli.cmd { Command::New { common, specs } => { let progress = get_progress(&common); let specs = get_specs(&common, &specs)?; cmd::new(&specs, &progress)?; } Command::Recreate { common, specs } => { let progress = get_progress(&common); let specs = get_specs(&common, &specs)?; cmd::recreate(&specs, &progress)?; } Command::Config { common } => { let progress = get_progress(&common); let config = config(&common)?; cmd::config(&config, &progress)?; } Command::Spec { common, spec } => { let progress = get_progress(&common); let specs = get_specs(&common, &[spec])?; cmd::spec(&specs, &progress)?; } Command::List { common } => { let progress = get_progress(&common); let config = config(&common)?; cmd::list(&config, &progress)?; } Command::Delete { common, specs } => { let progress = get_progress(&common); let specs = get_specs(&common, &specs)?; cmd::delete(&specs, &progress)?; } Command::Start { common, specs } => { let progress = get_progress(&common); let specs = get_specs(&common, &specs)?; cmd::start(&specs, &progress)?; } Command::Reboot { common, specs } => { let progress = get_progress(&common); let specs = get_specs(&common, &specs)?; cmd::reboot(&specs, &progress)?; } Command::Shutdown { common, specs } => { let progress = get_progress(&common); let specs = get_specs(&common, &specs)?; cmd::shutdown(&specs, &progress)?; } Command::CloudInit { common, spec, dirname, } => { let progress = get_progress(&common); let specs = get_specs(&common, &[spec])?; cmd::cloud_init(&specs, &progress, &dirname)?; } } Ok(()) } fn get_progress(common: &CommonOptions) -> Progress { if common.quiet { Progress::new(MessageKind::OnlyErrors) } else if common.verbose { Progress::new(MessageKind::Everything) } else { Progress::new(MessageKind::Steps) } } fn get_specs(common: &CommonOptions, specs: &[PathBuf]) -> anyhow::Result> { let config = config(common)?; let mut all_specs = vec![]; for spec in specs { let mut specs = Specification::from_file(&config, spec)?; all_specs.append(&mut specs); } Ok(all_specs) } fn config(common: &CommonOptions) -> anyhow::Result { let filename = config_filename(common); let config = Configuration::from_file(&filename) .with_context(|| format!("reading configuration file {}", filename.display()))?; Ok(config) } fn config_filename(common: &CommonOptions) -> PathBuf { if let Some(ref filename) = common.config { filename.to_path_buf() } else if let Some(dirs) = ProjectDirs::from(QUALIFIER, ORG, APP) { dirs.config_dir().join("config.yaml") } else { PathBuf::from("xxx") } }