use std::fmt;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::probe::{DebugProbeInfo, usb_util::to_hex};
use nusb::DeviceInfo;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DebugProbeSelector {
pub vendor_id: u16,
pub product_id: u16,
pub interface: Option<u8>,
pub serial_number: Option<String>,
}
impl DebugProbeSelector {
pub fn matches(&self, info: &DeviceInfo) -> bool {
fn matches_with_interface(
selector: &DebugProbeSelector,
info: &DeviceInfo,
interface: Option<u8>,
) -> bool {
selector.match_probe_selector(
info.vendor_id(),
info.product_id(),
interface,
info.serial_number(),
)
}
if self.interface.is_some() {
info.interfaces()
.any(|iface| matches_with_interface(self, info, Some(iface.interface_number())))
} else {
matches_with_interface(self, info, None)
}
}
pub fn matches_probe(&self, info: &DebugProbeInfo) -> bool {
self.match_probe_selector(
info.vendor_id,
info.product_id,
info.interface,
info.serial_number.as_deref(),
)
}
fn match_probe_selector(
&self,
vendor_id: u16,
product_id: u16,
interface: Option<u8>,
serial_number: Option<&str>,
) -> bool {
tracing::trace!(
"Matching probe selector:\nVendor ID: {vendor_id:04x} == {:04x}\nProduct ID: {product_id:04x} = {:04x}\nInterface: {interface:?} == {:?}\nSerial Number: {serial_number:?} == {:?}",
self.vendor_id,
self.product_id,
self.interface,
self.serial_number
);
vendor_id == self.vendor_id
&& product_id == self.product_id
&& self
.interface
.map(|iface| interface == Some(iface))
.unwrap_or(true) && self
.serial_number
.as_ref()
.map(|s| {
if let Some(serial_number) = serial_number {
serial_number == s || to_hex(serial_number).as_str() == s
} else {
s.is_empty()
}
})
.unwrap_or(true)
}
}
impl std::str::FromStr for DebugProbeSelector {
type Err = DebugProbeSelectorParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut split = s.splitn(3, ':');
let vendor_id = split.next().unwrap(); let mut product_id = split.next().ok_or(DebugProbeSelectorParseError::Format)?;
let interface = if let Some((id, iface)) = product_id.split_once("-") {
product_id = id;
if iface.is_empty() {
Ok(None)
} else {
iface.parse::<u8>().map(Some)
}
} else {
Ok(None)
}?;
let serial_number = split.next().map(|s| s.to_string());
Ok(DebugProbeSelector {
vendor_id: u16::from_str_radix(vendor_id, 16)?,
product_id: u16::from_str_radix(product_id, 16)?,
serial_number,
interface,
})
}
}
impl From<DebugProbeInfo> for DebugProbeSelector {
fn from(selector: DebugProbeInfo) -> Self {
DebugProbeSelector {
vendor_id: selector.vendor_id,
product_id: selector.product_id,
serial_number: selector.serial_number,
interface: selector.interface,
}
}
}
impl From<&DebugProbeInfo> for DebugProbeSelector {
fn from(selector: &DebugProbeInfo) -> Self {
DebugProbeSelector {
vendor_id: selector.vendor_id,
product_id: selector.product_id,
serial_number: selector.serial_number.clone(),
interface: selector.interface,
}
}
}
impl fmt::Display for DebugProbeSelector {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:04x}:{:04x}", self.vendor_id, self.product_id)?;
if let Some(interface) = self.interface {
write!(f, "-{interface}")?;
}
if let Some(ref sn) = self.serial_number {
write!(f, ":{sn}")?;
}
Ok(())
}
}
impl Serialize for DebugProbeSelector {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'a> Deserialize<'a> for DebugProbeSelector {
fn deserialize<D>(deserializer: D) -> Result<DebugProbeSelector, D::Error>
where
D: Deserializer<'a>,
{
let s = String::deserialize(deserializer)?;
s.parse().map_err(serde::de::Error::custom)
}
}
#[derive(thiserror::Error, Debug, docsplay::Display)]
pub enum DebugProbeSelectorParseError {
ParseInt(#[from] std::num::ParseIntError),
Format,
}
#[cfg(test)]
mod test {
use crate::probe::DebugProbeSelector;
#[test]
fn test_parsing_many_colons() {
let selector: DebugProbeSelector = "303a:1001:DC:DA:0C:D3:FE:D8".parse().unwrap();
assert_eq!(selector.vendor_id, 0x303a);
assert_eq!(selector.product_id, 0x1001);
assert_eq!(
selector.serial_number,
Some("DC:DA:0C:D3:FE:D8".to_string())
);
}
#[test]
fn missing_serial_is_none() {
let selector: DebugProbeSelector = "303a:1001".parse().unwrap();
assert_eq!(selector.vendor_id, 0x303a);
assert_eq!(selector.product_id, 0x1001);
assert_eq!(selector.serial_number, None);
let matches = selector.match_probe_selector(0x303a, 0x1001, None, None);
let matches_with_serial =
selector.match_probe_selector(0x303a, 0x1001, None, Some("serial"));
assert!(matches);
assert!(matches_with_serial);
}
#[test]
fn empty_serial_is_some() {
let selector: DebugProbeSelector = "303a:1001:".parse().unwrap();
assert_eq!(selector.vendor_id, 0x303a);
assert_eq!(selector.product_id, 0x1001);
assert_eq!(selector.serial_number, Some(String::new()));
let matches = selector.match_probe_selector(0x303a, 0x1001, None, None);
let matches_with_serial =
selector.match_probe_selector(0x303a, 0x1001, None, Some("serial"));
assert!(matches);
assert!(!matches_with_serial);
}
#[test]
fn missing_interface_is_none() {
let selector: DebugProbeSelector = "303a:1001".parse().unwrap();
assert_eq!(selector.vendor_id, 0x303a);
assert_eq!(selector.product_id, 0x1001);
assert_eq!(selector.interface, None);
let matches = selector.match_probe_selector(0x303a, 0x1001, None, None);
let matches_with_interface = selector.match_probe_selector(0x303a, 0x1001, Some(0), None);
assert!(matches);
assert!(matches_with_interface);
}
#[test]
fn empty_interface_is_none() {
let selector: DebugProbeSelector = "303a:1001-".parse().unwrap();
assert_eq!(selector.vendor_id, 0x303a);
assert_eq!(selector.product_id, 0x1001);
assert_eq!(selector.interface, None);
let matches = selector.match_probe_selector(0x303a, 0x1001, None, None);
let matches_with_interface = selector.match_probe_selector(0x303a, 0x1001, Some(0), None);
assert!(matches);
assert!(matches_with_interface);
}
#[test]
fn set_interface_matches() {
let selector: DebugProbeSelector = "303a:1001-0".parse().unwrap();
assert_eq!(selector.vendor_id, 0x303a);
assert_eq!(selector.product_id, 0x1001);
assert_eq!(selector.interface, Some(0));
let no_match = selector.match_probe_selector(0x303a, 0x1001, None, None);
let matches_with_interface = selector.match_probe_selector(0x303a, 0x1001, Some(0), None);
let no_match_with_wrong_interface =
selector.match_probe_selector(0x303a, 0x1001, Some(1), None);
assert!(!no_match);
assert!(matches_with_interface);
assert!(!no_match_with_wrong_interface);
}
#[test]
fn display_round_trips_with_interface() {
let selector: DebugProbeSelector = "0403:6010-1".parse().unwrap();
assert_eq!(selector.interface, Some(1));
assert_eq!(selector.to_string(), "0403:6010-1");
let reparsed: DebugProbeSelector = selector.to_string().parse().unwrap();
assert_eq!(reparsed.vendor_id, selector.vendor_id);
assert_eq!(reparsed.product_id, selector.product_id);
assert_eq!(reparsed.interface, selector.interface);
assert_eq!(reparsed.serial_number, selector.serial_number);
}
#[test]
fn display_round_trips_with_interface_and_serial() {
let selector: DebugProbeSelector = "0403:6010-1:ABCD1234".parse().unwrap();
assert_eq!(selector.interface, Some(1));
assert_eq!(selector.serial_number, Some("ABCD1234".to_string()));
assert_eq!(selector.to_string(), "0403:6010-1:ABCD1234");
let reparsed: DebugProbeSelector = selector.to_string().parse().unwrap();
assert_eq!(reparsed.vendor_id, selector.vendor_id);
assert_eq!(reparsed.product_id, selector.product_id);
assert_eq!(reparsed.interface, selector.interface);
assert_eq!(reparsed.serial_number, selector.serial_number);
}
#[test]
fn display_without_interface_unchanged() {
let selector: DebugProbeSelector = "0403:6010:ABCD1234".parse().unwrap();
assert_eq!(selector.interface, None);
assert_eq!(selector.to_string(), "0403:6010:ABCD1234");
}
}