use std::{ffi::OsString, net::IpAddr, path::PathBuf, str::FromStr, time::Duration};
use clap::{ArgAction, Parser};
use synd_stdx::time::humantime;
use crate::{
config::{self, env::env_key},
serve,
};
#[derive(Parser, Debug)]
#[command(version, propagate_version = true, disable_help_subcommand = true)]
pub struct Args {
#[command(flatten)]
pub kvsd: KvsdOptions,
#[command(flatten)]
pub bind: BindOptions,
#[command(flatten)]
pub serve: ServeOptions,
#[command(flatten)]
pub tls: TlsOptions,
#[command(flatten)]
pub o11y: ObservabilityOptions,
#[command(flatten)]
pub cache: CacheOptions,
#[arg(hide = true, long = "dry-run", hide_long_help = true)]
pub dry_run: bool,
}
#[derive(clap::Args, Debug, Clone)]
#[command(next_help_heading = "Kvsd options")]
pub struct KvsdOptions {
#[arg(long = "kvsd-host", env = env_key!("KVSD_HOST"))]
pub kvsd_host: String,
#[arg(long = "kvsd-port", env = env_key!("KVSD_PORT"))]
pub kvsd_port: u16,
#[arg(long = "kvsd-username", alias = "kvsd-user", env = env_key!("KVSD_USER"))]
pub kvsd_username: String,
#[arg(long = "kvsd-password", alias = "kvsd-pass", env = env_key!("KVSD_PASS"))]
pub kvsd_password: String,
}
#[derive(clap::Args, Debug, Clone)]
#[command(next_help_heading = "Bind options")]
pub struct BindOptions {
#[arg(long, value_parser = IpAddr::from_str, default_value = config::serve::DEFAULT_ADDR, env = env_key!("BIND_ADDR"))]
pub addr: IpAddr,
#[arg(long, default_value_t = config::serve::DEFAULT_PORT, env = env_key!("BIND_PORT"))]
pub port: u16,
}
#[derive(clap::Args, Debug, Clone)]
#[command(next_help_heading = "Serve options")]
pub struct ServeOptions {
#[arg(long, value_parser = humantime::parse_duration, default_value = config::serve::DEFAULT_REQUEST_TIMEOUT)]
pub timeout: Duration,
#[arg(long, default_value_t = config::serve::DEFAULT_REQUEST_BODY_LIMIT_BYTES)]
pub body_limit_bytes: usize,
#[arg(long, default_value_t = config::serve::DEFAULT_REQUEST_CONCURRENCY_LIMIT)]
pub concurrency_limit: usize,
}
#[derive(clap::Args, Debug)]
#[command(next_help_heading = "Tls options")]
pub struct TlsOptions {
#[arg(long = "tls-cert", env = env_key!("TLS_CERT"), value_name = "CERT_PATH")]
pub certificate: PathBuf,
#[arg(long = "tls-key", env = env_key!("TLS_KEY"), value_name = "KEY_PATH")]
pub private_key: PathBuf,
}
#[derive(clap::Args, Debug)]
#[command(next_help_heading = "Observability options")]
pub struct ObservabilityOptions {
#[arg(long, env = env_key!("LOG_SHOW_LOCATION"), default_value_t = false, action = ArgAction::Set )]
pub show_code_location: bool,
#[arg(long, env = env_key!("LOG_SHOW_TARGET"), default_value_t = true, action = ArgAction::Set)]
pub show_target: bool,
#[arg(long, env = "OTEL_EXPORTER_OTLP_ENDPOINT")]
pub otlp_endpoint: Option<String>,
#[arg(long, env = "OTEL_TRACES_SAMPLER_ARG", default_value_t = 1.0)]
pub trace_sampler_ratio: f64,
}
#[derive(clap::Args, Debug, Clone)]
#[command(next_help_heading = "Cache options")]
pub struct CacheOptions {
#[arg(long, default_value_t = config::cache::DEFAULT_FEED_CACHE_SIZE_MB, env = env_key!("FEED_CACHE_SIZE") )]
pub feed_cache_size_mb: u64,
#[arg(long, value_parser = humantime::parse_duration, default_value = config::cache::DEFAULT_FEED_CACHE_TTL, env = env_key!("FEED_CACHE_TTL"))]
pub feed_cache_ttl: Duration,
#[arg(long, value_parser = humantime::parse_duration, default_value = config::cache::DEFAULT_FEED_CACHE_REFRESH_INTERVAL, env = env_key!("FEED_CACHE_REFRESH_INTERVAL"))]
pub feed_cache_refresh_interval: Duration,
}
pub fn try_parse<I, T>(iter: I) -> Result<Args, clap::Error>
where
I: IntoIterator<Item = T>,
T: Into<OsString> + Clone,
{
Args::try_parse_from(iter)
}
impl From<BindOptions> for serve::BindOptions {
fn from(BindOptions { addr, port }: BindOptions) -> Self {
Self { port, addr }
}
}
impl From<ServeOptions> for serve::ServeOptions {
fn from(
ServeOptions {
timeout,
body_limit_bytes,
concurrency_limit,
}: ServeOptions,
) -> Self {
Self {
timeout,
body_limit_bytes,
concurrency_limit,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_parse() {
assert_eq!(
try_parse(["synd-api", "--version"]).unwrap_err().kind(),
clap::error::ErrorKind::DisplayVersion
);
assert_eq!(
try_parse(["synd-api", "--help"]).unwrap_err().kind(),
clap::error::ErrorKind::DisplayHelp,
);
}
}