use std::fmt;
use std::sync::Arc;
use hidpp::{
channel::HidppChannel,
receiver::{self, Receiver},
};
use crate::transport::{enumerate_hidpp_devices, open_hidpp_channel};
pub const DIRECT_DEVICE_INDEX: u8 = 0xff;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DeviceRoute {
Bolt { receiver_uid: String, slot: u8 },
Direct { vendor_id: u16, product_id: u16 },
}
impl DeviceRoute {
#[must_use]
pub fn device_index(&self) -> u8 {
match self {
Self::Bolt { slot, .. } => *slot,
Self::Direct { .. } => DIRECT_DEVICE_INDEX,
}
}
}
impl fmt::Display for DeviceRoute {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Bolt { receiver_uid, slot } => {
write!(f, "slot {slot} on receiver {receiver_uid}")
}
Self::Direct {
vendor_id,
product_id,
} => write!(f, "direct {vendor_id:04x}:{product_id:04x}"),
}
}
}
pub(crate) async fn open_route_channel(
route: &DeviceRoute,
) -> Result<Option<Arc<HidppChannel>>, async_hid::HidError> {
let candidates = enumerate_hidpp_devices().await?;
for dev in candidates {
if let DeviceRoute::Direct {
vendor_id,
product_id,
} = route
&& (dev.vendor_id != *vendor_id || dev.product_id != *product_id)
{
continue;
}
let Some((_, channel)) = open_hidpp_channel(dev).await? else {
continue;
};
match route {
DeviceRoute::Bolt { receiver_uid, .. } => {
let Some(Receiver::Bolt(bolt)) = receiver::detect(Arc::clone(&channel)) else {
continue;
};
if let Ok(uid) = bolt.get_unique_id().await
&& uid.eq_ignore_ascii_case(receiver_uid)
{
return Ok(Some(channel));
}
}
DeviceRoute::Direct { .. } => return Ok(Some(channel)),
}
}
Ok(None)
}