use std::sync::mpsc::Sender;
use std::sync::Arc;
use futures::StreamExt;
use log::{info, warn};
use mtp_rs::mtp::{watch_devices, HotplugEvent, MtpDeviceInfo};
use crate::daemon::dryrun::{DeviceFacts, DryRunCommand};
use crate::daemon::supervisor::{Command, DeviceChange, DeviceIdent, DeviceSource};
use crate::device::{DeviceOpener, UnplugSwitch};
pub fn facts_of(info: &MtpDeviceInfo) -> DeviceFacts {
DeviceFacts {
serial: info.serial_number.clone(),
vendor_id: info.vendor_id,
product_id: info.product_id,
location_id: info.location_id,
manufacturer: info.manufacturer.clone(),
product: info.product.clone(),
speed: info.speed.map(|s| format!("{s:?}")),
match_reason: Some(info.match_reason.as_str().to_string()),
label: info.display(),
}
}
pub fn ident_of(info: &MtpDeviceInfo) -> DeviceIdent {
facts_of(info).ident()
}
pub struct UsbSource;
impl DeviceSource for UsbSource {
fn opener(&self, ident: &DeviceIdent) -> Arc<dyn DeviceOpener> {
Arc::new(crate::device::UsbOpener::new(
ident.serial.clone(),
UnplugSwitch::default(),
))
}
}
pub fn spawn_hotplug_watch(
rt: &tokio::runtime::Handle,
commands: Sender<Command>,
) -> Result<(), mtp_rs::Error> {
let mut watch = watch_devices()?;
rt.spawn(async move {
while let Some(event) = watch.next().await {
let change = match event {
HotplugEvent::Arrived(info) => {
info!("Plugged in: {}", info.display());
DeviceChange::Arrived(ident_of(&info))
}
HotplugEvent::Left(info) => {
info!("Unplugged: {}", info.display());
DeviceChange::Left(ident_of(&info))
}
};
if commands.send(Command::Device(change)).is_err() {
return;
}
}
warn!("The USB hotplug watch ended; no further devices will be picked up.");
});
Ok(())
}
pub fn spawn_dry_run_watch(
rt: &tokio::runtime::Handle,
events: Sender<DryRunCommand>,
) -> Result<(), mtp_rs::Error> {
let mut watch = watch_devices()?;
rt.spawn(async move {
while let Some(event) = watch.next().await {
let command = match event {
HotplugEvent::Arrived(info) => DryRunCommand::Arrived(facts_of(&info)),
HotplugEvent::Left(info) => DryRunCommand::Left(facts_of(&info)),
};
if events.send(command).is_err() {
return;
}
}
let _ = events.send(DryRunCommand::Stop(
"the USB hotplug watch ended".to_string(),
));
});
Ok(())
}