use std::fmt;
use hidapi;
use crate::{Result, Uuid, UuidSelectable};
const FIDO_USAGE_PAGE: u16 = 0xF1D0;
const FIDO_USAGE: u16 = 0x1;
pub struct Session {
session: hidapi::HidApi,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Info {
pub path: std::ffi::CString,
pub vid: u16,
pub pid: u16,
pub serial: String,
pub manufacturer: String,
pub product: String,
}
pub struct Device {
pub(crate) device: hidapi::HidDevice,
info: Info,
}
pub fn list() -> Vec<Device> {
Session::new()
.map(|session| session.devices())
.unwrap_or_else(|_| vec![])
}
impl From<hidapi::DeviceInfo> for Info {
fn from(info: hidapi::DeviceInfo) -> Self {
Self {
path: info.path().to_owned(),
vid: info.vendor_id(),
pid: info.product_id(),
manufacturer: info.manufacturer_string().unwrap_or("").to_string(),
product: info.product_string().unwrap_or("").to_string(),
serial: info.serial_number().unwrap_or("").to_string(),
}
}
}
impl Device {
pub fn info(&self) -> &Info {
&self.info
}
}
impl fmt::Debug for Device {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.info.fmt(f)
}
}
impl Session {
pub fn is_available() -> bool {
Self::new().is_ok()
}
pub fn new() -> Result<Self> {
Ok(Self {
session: hidapi::HidApi::new()?,
})
}
pub fn infos(&self) -> Vec<Info> {
self.session
.device_list()
.filter(|info| info.usage_page() == FIDO_USAGE_PAGE && info.usage() == FIDO_USAGE)
.map(|info| info.clone().into())
.collect()
}
pub fn devices(&self) -> Vec<Device> {
self.infos()
.into_iter()
.filter_map(|info| {
self.session
.open_path(&info.path)
.map(|device| Device { device, info })
.ok()
})
.collect()
}
}
impl UuidSelectable for Device {
fn try_uuid(&mut self) -> Result<Uuid> {
let maybe_uuid = hex::decode(&self.info().serial)?;
Ok(Uuid::from_slice(&maybe_uuid)?)
}
fn list() -> Vec<Self> {
list()
}
}