use super::settings::Settings;
use super::{BootstrapResult, ServiceInfo};
use anyhow::anyhow;
use clap::error::ErrorKind;
use clap::Command;
use std::ffi::OsString;
pub use clap::{Arg, ArgAction, ArgMatches};
const GENERATE_CONFIG_OPT_ID: &str = "generate";
const USE_CONFIG_OPT_ID: &str = "config";
pub struct Cli<S: Settings> {
pub settings: S,
pub arg_matches: ArgMatches,
}
impl<S: Settings> Cli<S> {
pub fn new(service_info: &ServiceInfo, custom_args: Vec<Arg>) -> BootstrapResult<Self> {
Self::new_from_os_args(service_info, custom_args, std::env::args_os())
}
pub fn new_from_os_args(
service_info: &ServiceInfo,
custom_args: Vec<Arg>,
os_args: impl IntoIterator<Item = impl Into<OsString> + Clone>,
) -> BootstrapResult<Self> {
let mut cmd = Command::new(service_info.name)
.version(service_info.version)
.author(service_info.author)
.about(service_info.description)
.arg(
Arg::new("config")
.required_unless_present(GENERATE_CONFIG_OPT_ID)
.action(ArgAction::Set)
.long("config")
.short('c')
.help("Specifies the config to run the service with"),
)
.arg(
Arg::new(GENERATE_CONFIG_OPT_ID)
.action(ArgAction::Set)
.long("generate")
.short('g')
.help("Generates a new default config for the service"),
);
for arg in custom_args {
cmd = cmd.arg(arg);
}
let arg_matches = get_arg_matches(cmd, os_args)?;
let settings = get_settings(&arg_matches)?;
Ok(Self {
settings,
arg_matches,
})
}
}
fn get_arg_matches(
cmd: Command,
os_args: impl IntoIterator<Item = impl Into<OsString> + Clone>,
) -> BootstrapResult<ArgMatches> {
cmd.try_get_matches_from(os_args).map_err(|e| {
let kind = e.kind();
if kind == ErrorKind::DisplayHelp || kind == ErrorKind::DisplayVersion {
e.exit();
}
e.into()
})
}
fn get_settings<S: Settings>(arg_matches: &ArgMatches) -> BootstrapResult<S> {
if let Some(path) = arg_matches.get_one::<String>(GENERATE_CONFIG_OPT_ID) {
let settings = S::default();
crate::settings::to_yaml_file(&settings, path)?;
return Ok(settings);
}
if let Some(path) = arg_matches.get_one::<String>(USE_CONFIG_OPT_ID) {
return crate::settings::from_file(path).map_err(|e| anyhow!(e));
}
unreachable!("clap should require config options to be present")
}