use anyhow::{Result, bail};
use clap::builder::{PossibleValuesParser, TypedValueParser};
use crate::engine::BusProtocol;
#[derive(Debug, Clone, Default, clap::Args)]
pub struct ProtocolArgs {
#[arg(
long = "protocol",
value_name = "PROTOCOL",
value_parser = PossibleValuesParser::new(["nmea2000", "j1939", "quick"])
.map(|s| s.parse::<BusProtocol>().expect("listed in PossibleValuesParser")),
)]
protocol: Vec<BusProtocol>,
#[arg(long, hide = true)]
j1939: bool,
}
impl ProtocolArgs {
pub fn resolve(&self) -> Result<BusProtocol> {
let mut chosen = self.protocol.clone();
if self.j1939 {
chosen.push(BusProtocol::J1939);
}
chosen.dedup();
match chosen.as_slice() {
[] => Ok(BusProtocol::Nmea2000),
[one] => Ok(*one),
many => {
let names: Vec<_> = many.iter().map(|p| p.as_str()).collect();
bail!(
"--protocol {}: combining protocols on one bus is not supported yet",
names.join(" --protocol ")
)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use clap::Parser;
#[derive(Debug, Parser)]
struct Cli {
#[command(flatten)]
protocol: ProtocolArgs,
}
fn protocol(args: &[&str]) -> Result<BusProtocol> {
let mut argv = vec!["test"];
argv.extend_from_slice(args);
Cli::try_parse_from(argv)?.protocol.resolve()
}
#[test]
fn defaults_to_nmea2000() {
assert_eq!(protocol(&[]).unwrap(), BusProtocol::Nmea2000);
}
#[test]
fn picks_the_named_protocol() {
assert_eq!(
protocol(&["--protocol", "j1939"]).unwrap(),
BusProtocol::J1939
);
assert_eq!(
protocol(&["--protocol", "nmea2000"]).unwrap(),
BusProtocol::Nmea2000
);
}
#[test]
fn j1939_flag_is_an_alias() {
assert_eq!(protocol(&["--j1939"]).unwrap(), BusProtocol::J1939);
assert_eq!(
protocol(&["--j1939", "--protocol", "j1939"]).unwrap(),
BusProtocol::J1939
);
}
#[test]
fn refuses_two_protocols() {
let err = protocol(&["--protocol", "nmea2000", "--protocol", "j1939"]).unwrap_err();
assert!(err.to_string().contains("not supported yet"), "{err}");
assert!(protocol(&["--j1939", "--protocol", "nmea2000"]).is_err());
}
#[test]
fn refuses_unknown_names() {
assert!(protocol(&["--protocol", "canopen"]).is_err());
}
#[test]
fn takes_quick() {
assert_eq!(
protocol(&["--protocol", "quick"]).unwrap(),
BusProtocol::Quick
);
}
#[test]
fn bus_is_not_an_option() {
assert!(protocol(&["--bus", "j1939"]).is_err());
}
}