use std::fmt::{Debug, Display, Formatter};
use std::net::SocketAddr;
use std::time::Duration;
use clap::{Parser, ValueEnum};
use roughenough_protocol::tags::Version;
#[derive(Parser, Debug, Clone)]
#[command(version = "2.0.0", about = "Roughenough roughtime server")]
pub struct Args {
#[clap(
short = 'b',
long,
value_name = "N",
env = "ROUGHENOUGH_BATCH_SIZE",
default_value_t = 64,
value_parser = clap::value_parser!(u8).range(1..=64)
)]
pub batch_size: u8,
#[clap(
short = 'i',
long,
env = "ROUGHENOUGH_INTERFACE",
default_value = "0.0.0.0"
)]
pub interface: String,
#[clap(short = 'p', long, env = "ROUGHENOUGH_PORT", default_value = "2003")]
pub port: u16,
#[clap(
short = 'j',
long,
value_name = "N",
env = "ROUGHENOUGH_NUM_THREADS",
default_value_t = default_num_threads()
)]
pub num_threads: u16,
#[clap(
value_enum,
short = 'P',
long,
value_name = "PROTOCOL",
env = "ROUGHENOUGH_PROTOCOL",
default_value_t = ProtocolVersionArg::V14,
)]
pub protocol: ProtocolVersionArg,
#[clap(
long,
value_name = "N",
env = "ROUGHENOUGH_FIXED_OFFSET",
default_value_t = 0
)]
pub fixed_offset: i16,
#[clap(short, long, conflicts_with = "verbose", default_value_t = false)]
pub quiet: bool,
#[clap(
long,
value_name = "HOURS",
env = "ROUGHENOUGH_ROTATION_INTERVAL",
default_value = "24"
)]
pub rotation_interval: u16,
#[clap(
long,
value_name = "SECONDS",
env = "ROUGHENOUGH_METRICS_INTERVAL",
default_value = "60"
)]
pub metrics_interval: u64,
#[clap(
long,
value_name = "SEED",
env = "ROUGHENOUGH_SEED",
default_value = ""
)]
pub seed: String,
#[clap(
value_enum,
long,
value_name = "TYPE",
env = "ROUGHENOUGH_SEED_BACKEND",
default_value_t = SeedBackendArg::Memory
)]
pub seed_backend: SeedBackendArg,
#[clap(
long,
value_name = "PATH",
env = "ROUGHENOUGH_METRICS_OUTPUT",
help = "Directory where JSON metrics files will be written (e.g., /var/log/roughenough)"
)]
pub metrics_output: Option<String>,
#[clap(
short = 'v',
long,
conflicts_with = "quiet",
action = clap::ArgAction::Count,
help = "Output details about requests and responses; specify multiple times for more detail"
)]
pub verbose: u8,
}
#[derive(ValueEnum, Debug, Clone)]
pub enum ProtocolVersionArg {
#[value(name = "14")]
V14,
}
#[derive(ValueEnum, Debug, Clone)]
pub enum SeedBackendArg {
#[value(name = "memory")]
Memory,
#[value(name = "krs")]
Krs,
#[value(name = "ssh-agent")]
SshAgent,
}
impl Display for SeedBackendArg {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let value = self.to_possible_value().unwrap();
f.write_str(value.get_name())
}
}
impl Args {
pub fn udp_socket_addr(&self) -> SocketAddr {
let addr = self
.interface
.parse()
.expect("invalid IP address or interface name");
SocketAddr::new(addr, self.port)
}
pub fn version(&self) -> Version {
match self.protocol {
ProtocolVersionArg::V14 => Version::RfcDraft14,
}
}
pub fn rotation_interval(&self) -> Duration {
Duration::from_secs(self.rotation_interval as u64 * 60 * 60)
}
}
fn default_num_threads() -> u16 {
std::thread::available_parallelism().unwrap().get() as u16
}