Skip to content

Commit b12e896

Browse files
committed
0.1.0 release
1 parent ee5dd01 commit b12e896

File tree

6 files changed

+94
-80
lines changed

6 files changed

+94
-80
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2019 Tim Fish
3+
Copyright (c) 2021 Tim Fish
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ currently connected USB devices
77

88
## Example
99
```rust
10-
let devices = usb_enumeration::enumerate();
10+
let devices = usb_enumeration::enumerate(None, None);
1111

1212
println!("{:#?}", devices);
1313

@@ -40,12 +40,12 @@ println!("{:#?}", devices);
4040
// etc...
4141
// ]
4242
```
43-
You can also subscribe events using the `Observer`:
43+
You can also subscribe to events using the `Observer`:
4444
```rust
4545
use usb_enumeration::{Observer, Event};
4646

47-
// Set the poll interval to 2 seconds
48-
let sub = Observer::new(2)
47+
let sub = Observer::new()
48+
.with_poll_interval(2)
4949
.with_vendor_id(0x1234)
5050
.with_product_id(0x5678)
5151
.subscribe();

src/lib.rs

Lines changed: 35 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
//! [![Actions Status](https://github.com/timfish/usb-enumeration/workflows/Build/badge.svg)](https://github.com/timfish/usb-enumeration/actions)
55
//!
66
//! # Example
7-
//! ```
8-
//! let devices = usb_enumeration::enumerate();
7+
//! ```no_run
8+
//! let devices = usb_enumeration::enumerate(None, None);
99
//!
1010
//! println!("{:#?}", devices);
1111
//!
@@ -38,12 +38,12 @@
3838
//! // etc...
3939
//! // ]
4040
//! ```
41-
//! You can also subscribe events using the `Observer`:
41+
//! You can also subscribe to events using the `Observer`:
4242
//! ```no_run
4343
//! use usb_enumeration::{Observer, Event};
4444
//!
45-
//! // Set the poll interval to 2 seconds
46-
//! let sub = Observer::new(2)
45+
//! let sub = Observer::new()
46+
//! .with_poll_interval(2)
4747
//! .with_vendor_id(0x1234)
4848
//! .with_product_id(0x5678)
4949
//! .subscribe();
@@ -83,39 +83,18 @@ use crate::linux::*;
8383

8484
/// # Enumerates connected USB devices
8585
///
86-
/// * `vendor_id` - USB Vendor ID to filter
87-
/// * `product_id` - USB Product ID to filter
86+
/// * `vendor_id` - Optional USB Vendor ID to filter
87+
/// * `product_id` - Optional USB Product ID to filter
8888
///
89+
/// ```no_run
90+
/// let devices = usb_enumeration::enumerate(None, None);
8991
/// ```
90-
/// let devices = usb_enumeration::enumerate();
92+
/// You can also optionally filter by vendor or product ID:
93+
/// ```no_run
94+
/// let devices = usb_enumeration::enumerate(Some(0x1234), None);
9195
/// ```
92-
/// There are also some handy filters:
93-
/// ```
94-
/// use usb_enumeration::Filters;
95-
///
96-
/// let devices = usb_enumeration::enumerate().with_vendor_id(0x1234);
97-
/// ```
98-
pub fn enumerate() -> Vec<USBDevice> {
99-
enumerate_platform()
100-
}
101-
102-
pub trait Filters {
103-
fn with_vendor_id(self, vendor_id: u16) -> Vec<USBDevice>;
104-
fn with_product_id(self, product_id: u16) -> Vec<USBDevice>;
105-
}
106-
107-
impl Filters for Vec<USBDevice> {
108-
fn with_vendor_id(self, vendor_id: u16) -> Vec<USBDevice> {
109-
self.into_iter()
110-
.filter(|d| d.vendor_id == vendor_id)
111-
.collect()
112-
}
113-
114-
fn with_product_id(self, product_id: u16) -> Vec<USBDevice> {
115-
self.into_iter()
116-
.filter(|d| d.product_id == product_id)
117-
.collect()
118-
}
96+
pub fn enumerate(vendor_id: Option<u16>, product_id: Option<u16>) -> Vec<USBDevice> {
97+
enumerate_platform(vendor_id, product_id)
11998
}
12099

121100
/// Events send from the Observer
@@ -139,21 +118,32 @@ pub struct Subscription {
139118

140119
#[derive(Debug, Clone)]
141120
pub struct Observer {
142-
poll_interval: u64,
121+
poll_interval: u32,
143122
vendor_id: Option<u16>,
144123
product_id: Option<u16>,
145124
}
146125

126+
impl Default for Observer {
127+
fn default() -> Self {
128+
Observer::new()
129+
}
130+
}
131+
147132
impl Observer {
148133
/// Create a new Observer with the poll interval specified in seconds
149-
pub fn new(poll_interval: u64) -> Self {
134+
pub fn new() -> Self {
150135
Observer {
151-
poll_interval,
136+
poll_interval: 1,
152137
vendor_id: None,
153138
product_id: None,
154139
}
155140
}
156141

142+
pub fn with_poll_interval(mut self, seconds: u32) -> Self {
143+
self.poll_interval = seconds;
144+
self
145+
}
146+
157147
/// Filter results by USB Vendor ID
158148
pub fn with_vendor_id(mut self, vendor_id: u16) -> Self {
159149
self.vendor_id = Some(vendor_id);
@@ -166,20 +156,6 @@ impl Observer {
166156
self
167157
}
168158

169-
fn enumerate(&self) -> Vec<USBDevice> {
170-
let mut devices = enumerate();
171-
172-
if let Some(vendor_id) = self.vendor_id {
173-
devices = devices.with_vendor_id(vendor_id);
174-
}
175-
176-
if let Some(product_id) = self.product_id {
177-
devices = devices.with_product_id(product_id);
178-
}
179-
180-
devices
181-
}
182-
183159
/// Start the background thread and poll for device changes
184160
pub fn subscribe(&self) -> Subscription {
185161
let (tx_event, rx_event) = unbounded();
@@ -190,7 +166,7 @@ impl Observer {
190166
.spawn({
191167
let this = self.clone();
192168
move || {
193-
let device_list = this.enumerate();
169+
let device_list = enumerate(this.vendor_id, this.product_id);
194170

195171
// Send initially connected devices
196172
if tx_event.send(Event::Initial(device_list.clone())).is_err() {
@@ -215,7 +191,9 @@ impl Observer {
215191
wait_seconds = this.poll_interval;
216192

217193
let next_devices: HashSet<USBDevice> =
218-
this.enumerate().into_iter().collect();
194+
enumerate(this.vendor_id, this.product_id)
195+
.into_iter()
196+
.collect();
219197

220198
// Send Disconnect for missing devices
221199
for device in &device_list {
@@ -251,17 +229,17 @@ mod tests {
251229

252230
#[test]
253231
fn test_enumerate() {
254-
let devices = enumerate();
232+
let devices = enumerate(None, None);
255233
println!("Enumerated devices: {:#?}", devices);
256234
assert!(!devices.is_empty());
257235
}
258236

259237
#[test]
260238
fn test_subscribe() {
261-
let subscription = Observer::new(1).subscribe();
239+
let subscription = Observer::new().subscribe();
262240
let mut iter = subscription.rx_event.iter();
263241

264-
let initial = iter.next().unwrap();
242+
let initial = iter.next().expect("Should get an Event");
265243
assert!(matches!(initial, Event::Initial(_)));
266244

267245
println!("Connect a USB device");

src/linux.rs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,13 @@ use crate::common::*;
33
use std::error::Error;
44
use udev::Enumerator;
55

6-
pub fn enumerate_platform() -> Vec<USBDevice> {
6+
pub fn enumerate_platform(vid: Option<u16>, pid: Option<u16>) -> Vec<USBDevice> {
77
let mut output = Vec::new();
88

99
let mut enumerator = Enumerator::new().expect("could not get udev enumerator");
1010

1111
for device in enumerator.scan_devices().expect("could not scan devices") {
1212
let _ = || -> Result<(), Box<dyn Error>> {
13-
let id = device
14-
.property_value("DEVPATH")
15-
.ok_or(ParseError)?
16-
.to_str()
17-
.ok_or(ParseError)?
18-
.to_string();
19-
2013
let vendor_id = get_pid_or_vid(
2114
device
2215
.property_value("ID_VENDOR_ID")
@@ -25,6 +18,12 @@ pub fn enumerate_platform() -> Vec<USBDevice> {
2518
.ok_or(ParseError)?,
2619
)?;
2720

21+
if let Some(vid) = vid {
22+
if vid != vendor_id {
23+
continue;
24+
}
25+
}
26+
2827
let product_id = get_pid_or_vid(
2928
device
3029
.property_value("ID_MODEL_ID")
@@ -33,6 +32,19 @@ pub fn enumerate_platform() -> Vec<USBDevice> {
3332
.ok_or(ParseError)?,
3433
)?;
3534

35+
if let Some(pid) = pid {
36+
if pid != product_id {
37+
continue;
38+
}
39+
}
40+
41+
let id = device
42+
.property_value("DEVPATH")
43+
.ok_or(ParseError)?
44+
.to_str()
45+
.ok_or(ParseError)?
46+
.to_string();
47+
3648
let mut description = device
3749
.property_value("ID_MODEL_FROM_DATABASE")
3850
.and_then(|s| s.to_str())

src/macos.rs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use io_kit_sys::{types::*, usb::lib::*, *};
44
use mach::kern_return::*;
55
use std::{error::Error, mem::MaybeUninit};
66

7-
pub fn enumerate_platform() -> Vec<USBDevice> {
7+
pub fn enumerate_platform(vid: Option<u16>, pid: Option<u16>) -> Vec<USBDevice> {
88
let mut output = Vec::new();
99

1010
unsafe {
@@ -35,14 +35,6 @@ pub fn enumerate_platform() -> Vec<USBDevice> {
3535
CFMutableDictionary::wrap_under_get_rule(props).to_immutable();
3636

3737
let _ = || -> Result<(), Box<dyn Error>> {
38-
let key = CFString::from_static_string("sessionID");
39-
let id = properties
40-
.find(&key)
41-
.and_then(|value_ref| value_ref.downcast::<CFNumber>())
42-
.ok_or(ParseError)?
43-
.to_i64()
44-
.ok_or(ParseError)?;
45-
4638
let key = CFString::from_static_string("idVendor");
4739
let vendor_id = properties
4840
.find(&key)
@@ -51,6 +43,12 @@ pub fn enumerate_platform() -> Vec<USBDevice> {
5143
.to_i32()
5244
.ok_or(ParseError)? as u16;
5345

46+
if let Some(vid) = vid {
47+
if vid != vendor_id {
48+
continue;
49+
}
50+
}
51+
5452
let key = CFString::from_static_string("idProduct");
5553
let product_id = properties
5654
.find(&key)
@@ -59,6 +57,20 @@ pub fn enumerate_platform() -> Vec<USBDevice> {
5957
.to_i32()
6058
.ok_or(ParseError)? as u16;
6159

60+
if let Some(pid) = pid {
61+
if pid != product_id {
62+
continue;
63+
}
64+
}
65+
66+
let key = CFString::from_static_string("sessionID");
67+
let id = properties
68+
.find(&key)
69+
.and_then(|value_ref| value_ref.downcast::<CFNumber>())
70+
.ok_or(ParseError)?
71+
.to_i64()
72+
.ok_or(ParseError)?;
73+
6274
let key = CFString::from_static_string("USB Product Name");
6375
let description = properties
6476
.find(&key)

src/windows.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::{
88
};
99
use winapi::um::setupapi::*;
1010

11-
pub fn enumerate_platform() -> Vec<USBDevice> {
11+
pub fn enumerate_platform(vid: Option<u16>, pid: Option<u16>) -> Vec<USBDevice> {
1212
let mut output: Vec<USBDevice> = Vec::new();
1313

1414
let usb: Vec<u16> = OsStr::new("USB\0").encode_wide().collect();
@@ -43,6 +43,18 @@ pub fn enumerate_platform() -> Vec<USBDevice> {
4343
} > 0
4444
{
4545
if let Ok((vendor_id, product_id)) = extract_vid_pid(buf) {
46+
if let Some(vid) = vid {
47+
if vid != vendor_id {
48+
continue;
49+
}
50+
}
51+
52+
if let Some(pid) = pid {
53+
if pid != product_id {
54+
continue;
55+
}
56+
}
57+
4658
buf = vec![0; 1000];
4759

4860
if unsafe {

0 commit comments

Comments
 (0)