use std::{env, net::SocketAddr, path::PathBuf};
use anyhow::{bail, Context};
use base64::Engine;
use clap::{Args, Parser};
use crate::config_file::ConfigFile;
#[derive(Clone, derivative::Derivative)]
#[derivative(Debug)]
pub struct AppConfig {
pub bind_addr: SocketAddr,
pub config_file: ConfigFile,
}
impl AppConfig {
pub fn try_parse() -> anyhow::Result<Self> {
#[allow(clippy::expect_used)]
AppArgs::parse().try_into()
}
}
#[derive(Parser, Debug)]
#[clap(author, about, long_version = long_version(), version = version(), help_template=help())]
pub struct AppArgs {
#[clap(long, value_parser, env = "ROUTEROS_DHCP_LEASES_WEBUI_BIND_ADDR")]
bind_addr: SocketAddr,
#[command(flatten)]
config_file: ConfigFileArg,
}
#[derive(Args, Debug)]
#[group(required = true, multiple = false)]
pub struct ConfigFileArg {
#[clap(long, value_parser, env = "ROUTEROS_DHCP_LEASES_WEBUI_CONFIG_FILE")]
config_file: Option<PathBuf>,
#[clap(
long,
value_parser,
env = "ROUTEROS_DHCP_LEASES_WEBUI_CONFIG_FILE_CONTENT_B64"
)]
config_file_content_b64: Option<String>,
}
impl TryFrom<AppArgs> for AppConfig {
type Error = anyhow::Error;
fn try_from(value: AppArgs) -> Result<Self, Self::Error> {
let AppArgs {
bind_addr,
config_file,
} = value;
let config_file = if let Some(config_file_content) = config_file.config_file_content_b64 {
let content = base64::engine::general_purpose::STANDARD
.decode(config_file_content)
.with_context(|| "Failed to decode config file content from base64")?;
ConfigFile::from_yaml_reader(&content[..])
.with_context(|| "Failed to load config file from content")?
} else if let Some(config_file_path) = config_file.config_file {
if !config_file_path.is_file() {
bail!("Config file `{}` doesn't exist", config_file_path.display())
}
ConfigFile::load_from_yaml(&config_file_path)
.with_context(|| "Failed to load config file")?
} else {
bail!("No config file provided");
};
Ok(Self {
bind_addr,
config_file,
})
}
}
const fn version() -> &'static str {
concat!(
env!("CARGO_PKG_VERSION"),
" git:",
env!("VERGEN_GIT_DESCRIBE")
)
}
const fn long_version() -> &'static str {
concat!(
env!("CARGO_PKG_VERSION"),
"\n",
"\nAuthors:\t",
env!("CARGO_PKG_AUTHORS"),
"\nCommit SHA:\t",
env!("VERGEN_GIT_SHA"),
"\nCommit Date:\t",
env!("VERGEN_GIT_COMMIT_TIMESTAMP"),
"\nDescribe:\t",
env!("VERGEN_GIT_DESCRIBE"),
"\nrustc Version:\t",
env!("VERGEN_RUSTC_SEMVER"),
"\nrustc SHA:\t",
env!("VERGEN_RUSTC_COMMIT_HASH"),
"\ncargo Target:\t",
env!("VERGEN_CARGO_TARGET_TRIPLE"),
"\ncargo opt:\t",
env!("VERGEN_CARGO_OPT_LEVEL"),
)
}
const fn help() -> &'static str {
"{before-help}{name} {version}
{author-with-newline}{about-with-newline}
{usage-heading} {usage}
{all-args}{after-help}
"
}