#![forbid(unsafe_code)]
pub mod setupapi;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Bus {
Usb,
Mtp,
FireWire,
Thunderbolt,
Pcie,
Esata,
SdMmc,
Bluetooth,
ExpressCard,
ScsiSas,
Nvme,
Unknown,
}
impl Bus {
#[must_use]
pub fn from_enumerator(enumerator: &str) -> Self {
let e = enumerator.trim().to_ascii_uppercase();
match e.as_str() {
"USBSTOR" | "USB" => Self::Usb,
"1394" => Self::FireWire,
"THUNDERBOLT" => Self::Thunderbolt,
"PCI" | "PCIE" => Self::Pcie,
"SCSI" | "SAS" => Self::ScsiSas,
"NVME" => Self::Nvme,
"SD" | "MMC" | "SDBUS" => Self::SdMmc,
"ESATA" => Self::Esata,
"BTHENUM" | "BTHLE" | "BLUETOOTH" => Self::Bluetooth,
"EXPRESSCARD" => Self::ExpressCard,
"WPDBUSENUMROOT" | "MTP" => Self::Mtp,
_ => Self::Unknown,
}
}
#[must_use]
pub fn is_dma_capable(self) -> bool {
matches!(
self,
Self::FireWire | Self::Thunderbolt | Self::Pcie | Self::ExpressCard
)
}
#[must_use]
pub fn is_mass_storage(self) -> bool {
matches!(
self,
Self::Usb | Self::Esata | Self::SdMmc | Self::ScsiSas | Self::Nvme
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Confidence {
Authoritative,
Inferred,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Stamp {
pub value: i64,
pub confidence: Confidence,
}
impl Stamp {
#[must_use]
pub fn authoritative(value: i64) -> Self {
Self {
value,
confidence: Confidence::Authoritative,
}
}
#[must_use]
pub fn inferred(value: i64) -> Self {
Self {
value,
confidence: Confidence::Inferred,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MitreRef(pub &'static str);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DeviceConnection {
pub bus: Bus,
pub device_class_guid: Option<String>,
pub vid: Option<u16>,
pub pid: Option<u16>,
pub device_serial: Option<String>,
pub serial_is_os_generated: bool,
pub friendly_name: Option<String>,
pub device_instance_id: String,
pub first_install: Option<Stamp>,
pub last_install: Option<Stamp>,
pub last_arrival: Option<Stamp>,
pub last_removal: Option<Stamp>,
pub parent_id_prefix: Option<String>,
pub volume_guid: Option<String>,
pub drive_letter: Option<char>,
pub volume_serial: Option<u32>,
pub disk_signature: Option<u32>,
pub dma_capable: bool,
pub mitre: Vec<MitreRef>,
pub source: Provenance,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Provenance {
pub file: String,
pub line: usize,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn usb_enumerators_classify_as_usb() {
assert_eq!(Bus::from_enumerator("USBSTOR"), Bus::Usb);
assert_eq!(Bus::from_enumerator("USB"), Bus::Usb);
assert_eq!(Bus::from_enumerator("usbstor"), Bus::Usb); }
#[test]
fn bus_specific_enumerators_classify() {
assert_eq!(Bus::from_enumerator("1394"), Bus::FireWire);
assert_eq!(Bus::from_enumerator("SCSI"), Bus::ScsiSas);
assert_eq!(Bus::from_enumerator("PCI"), Bus::Pcie);
assert_eq!(Bus::from_enumerator("SD"), Bus::SdMmc);
assert_eq!(Bus::from_enumerator("WpdBusEnumRoot"), Bus::Mtp);
assert_eq!(Bus::from_enumerator("THUNDERBOLT"), Bus::Thunderbolt);
assert_eq!(Bus::from_enumerator("ESATA"), Bus::Esata);
assert_eq!(Bus::from_enumerator("EXPRESSCARD"), Bus::ExpressCard);
assert_eq!(Bus::from_enumerator("BTHENUM"), Bus::Bluetooth);
assert_eq!(Bus::from_enumerator("NVME"), Bus::Nvme);
}
#[test]
fn unknown_enumerator_is_unknown_never_panics() {
assert_eq!(Bus::from_enumerator("HID"), Bus::Unknown);
assert_eq!(Bus::from_enumerator(""), Bus::Unknown);
assert_eq!(Bus::from_enumerator(" "), Bus::Unknown);
}
#[test]
fn dma_capable_is_exactly_firewire_thunderbolt_pcie_expresscard() {
for b in [Bus::FireWire, Bus::Thunderbolt, Bus::Pcie, Bus::ExpressCard] {
assert!(b.is_dma_capable(), "{b:?} must be DMA-capable");
}
for b in [Bus::Usb, Bus::Esata, Bus::SdMmc, Bus::ScsiSas, Bus::Nvme] {
assert!(!b.is_dma_capable(), "{b:?} must NOT be DMA-capable");
}
for b in [Bus::Bluetooth, Bus::Mtp, Bus::Unknown] {
assert!(!b.is_dma_capable(), "{b:?} must NOT be DMA-capable");
}
}
#[test]
fn mass_storage_classes() {
for b in [Bus::Usb, Bus::Esata, Bus::SdMmc, Bus::ScsiSas, Bus::Nvme] {
assert!(b.is_mass_storage(), "{b:?} should be mass storage");
}
for b in [Bus::FireWire, Bus::Thunderbolt, Bus::Bluetooth, Bus::Mtp] {
assert!(!b.is_mass_storage(), "{b:?} should not be mass storage");
}
}
#[test]
fn stamp_carries_confidence() {
assert_eq!(
Stamp::authoritative(10).confidence,
Confidence::Authoritative
);
assert_eq!(Stamp::inferred(10).confidence, Confidence::Inferred);
}
}