use std::{net::IpAddr, path::PathBuf, time::Duration};
use clap::{Parser, ValueEnum};
use ipnet::IpNet;
use pnet::datalink::{self, NetworkInterface};
use reqwest::Url;
#[derive(Copy, Clone, ValueEnum)]
pub enum NetworkBackend {
Networkd,
}
#[derive(Parser)]
#[command(name = "wiresmith", author, about, version)]
pub struct CliArgs {
#[arg(long, default_value = "http://127.0.0.1:8500")]
pub consul_address: Url,
#[arg(long)]
pub consul_token: Option<String>,
#[arg(long, default_value = "wiresmith")]
pub consul_prefix: String,
#[arg(short, long, default_value = "10s", value_parser = humantime::parse_duration)]
pub update_period: Duration,
#[arg(short = 'i', long, default_value = "wg0")]
pub wg_interface: String,
#[arg(short = 'p', long, default_value = "51820")]
pub wg_port: u16,
#[arg(short = 'k', long, default_value = "25s", value_parser = keep_alive)]
pub keepalive: u64,
#[arg(long,
required_unless_present = "endpoint_address",
conflicts_with = "endpoint_address",
value_parser = network_interface
)]
pub endpoint_interface: Option<NetworkInterface>,
#[arg(
long,
required_unless_present = "endpoint_interface",
conflicts_with = "endpoint_interface"
)]
pub endpoint_address: Option<String>,
#[arg(long, default_value = "networkd")]
pub network_backend: NetworkBackend,
#[arg(long, default_value = "/etc/systemd/network/")]
pub networkd_dir: PathBuf,
#[arg(short, long)]
pub address: Option<IpAddr>,
#[arg(short, long)]
pub network: IpNet,
#[arg(short, long, action = clap::ArgAction::Count, value_parser = clap::value_parser!(u8).range(0..=2))]
pub verbose: u8,
}
fn network_interface(s: &str) -> Result<NetworkInterface, String> {
let interfaces = datalink::interfaces();
let interface = interfaces
.iter()
.find(|e| e.is_up() && !e.is_loopback() && !e.ips.is_empty() && e.name == s);
match interface {
Some(i) => Ok(i.clone()),
None => Err(format!("No usable interface found for '{}'", s)),
}
}
fn keep_alive(s: &str) -> Result<u64, humantime::DurationError> {
let duration = humantime::parse_duration(s)?;
Ok(duration.as_secs())
}