use std::time::{Duration, Instant};
use crate::device::{self, Device, APPLE_VID, DFU_PID};
use crate::error::{Error, Result};
fn restorable() -> Result<Vec<Device>> {
Ok(device::list()?
.into_iter()
.filter(|d| d.restorable())
.collect())
}
pub struct Watch {
events: std::pin::Pin<Box<nusb::hotplug::HotplugWatch>>,
already_present: Vec<u64>,
}
pub fn watch() -> Result<Watch> {
let events = nusb::watch_devices().map_err(|e| Error::Usb(e.to_string()))?;
let already_present = restorable()?.iter().filter_map(|d| d.ecid).collect();
Ok(Watch {
events: Box::pin(events),
already_present,
})
}
impl Watch {
pub fn wait(&mut self, timeout: Duration) -> Result<Device> {
use nusb::hotplug::HotplugEvent;
let deadline = Instant::now() + timeout;
loop {
while let Some(event) = self.next_event() {
if let HotplugEvent::Connected(info) = event {
if info.vendor_id() != APPLE_VID || info.product_id() != DFU_PID {
continue;
}
let dev = device::from_usb(&info);
if let Some(ecid) = dev.ecid {
if !self.already_present.contains(&ecid) {
return Ok(dev);
}
}
}
}
if let Some(dev) = restorable()?
.into_iter()
.find(|d| d.ecid.is_some_and(|e| !self.already_present.contains(&e)))
{
return Ok(dev);
}
if Instant::now() >= deadline {
return Err(Error::WaitTimeout);
}
std::thread::sleep(Duration::from_millis(100));
}
}
fn next_event(&mut self) -> Option<nusb::hotplug::HotplugEvent> {
use futures_core::Stream;
use std::task::{Context, Poll, Waker};
let mut cx = Context::from_waker(Waker::noop());
match self.events.as_mut().poll_next(&mut cx) {
Poll::Ready(event) => event,
Poll::Pending => None,
}
}
}