use clap::{Parser, Subcommand, ValueHint};
use twinleaf::device::DeviceRoute;
use crate::{parse_device_route, TioOpts};
#[derive(Parser, Debug)]
#[command(
version,
about = "Multiplexes access to a sensor, exposing the functionality of tio::proxy via TCP",
args_conflicts_with_subcommands = true
)]
pub struct ProxyCli {
#[command(subcommand)]
pub subcommands: Option<ProxySubcommands>,
#[arg(value_hint = ValueHint::Url, conflicts_with = "mounts")]
pub(crate) sensor_url: Option<String>,
#[arg(long = "mount", value_name = "LOCATOR=/N", value_parser = parse_mount)]
pub(crate) mounts: Vec<MountArg>,
#[arg(short = 'p', long = "port", default_value = "7855")]
pub(crate) port: u16,
#[arg(short = 'k', long)]
pub(crate) kick_slow: bool,
#[arg(
short = 's',
long = "subtree",
default_value = "/",
value_parser = parse_device_route,
conflicts_with = "mounts",
)]
pub(crate) subtree: DeviceRoute,
#[arg(short = 'v', long)]
pub(crate) verbose: bool,
#[arg(short = 'd', long)]
pub(crate) debug: bool,
#[arg(
short = 't',
long = "timestamp",
default_value = "%T%.3f ",
hide = true
)]
pub(crate) timestamp_format: String,
#[arg(short = 'T', long = "timeout", default_value = "30")]
pub(crate) reconnect_timeout: u64,
#[arg(long)]
pub(crate) dump: bool,
#[arg(long)]
pub(crate) dump_data: bool,
#[arg(long)]
pub(crate) dump_meta: bool,
#[arg(long)]
pub(crate) dump_hb: bool,
#[arg(short = 'a', long = "auto", hide = true)]
pub(crate) auto: bool,
#[arg(short = 'e', long = "enumerate", name = "enum", hide = true)]
pub(crate) enumerate: bool,
}
#[derive(Debug, Clone)]
pub struct MountArg {
pub locator: String,
pub prefix: DeviceRoute,
}
fn parse_mount(s: &str) -> Result<MountArg, String> {
let Some((locator, prefix_str)) = s.split_once('=') else {
return Err(format!(
"expected LOCATOR=/N (e.g. serial:///dev/ttyUSB0=/1), got {s:?}"
));
};
if locator.is_empty() {
return Err(format!("missing sensor locator before '=' in {s:?}"));
}
let prefix = DeviceRoute::from_str(prefix_str)
.map_err(|_| format!("invalid route prefix: {prefix_str:?}"))?;
if prefix.len() != 1 {
return Err(format!(
"mount prefix must be a single segment like /1, got {prefix_str:?}"
));
}
Ok(MountArg {
locator: locator.to_string(),
prefix,
})
}
#[derive(Subcommand, Debug)]
pub enum ProxySubcommands {
Nmea {
#[command(flatten)]
tio: TioOpts,
#[arg(short = 'p', long = "port", default_value = "7800")]
tcp_port: u16,
},
}