Skip to main content

backends/
backends.rs

1//! Drive both backends from one process, and open a device by falling back
2//! from one to the next.
3//!
4//! Backends are types, so a handle that can hold either is the caller's to
5//! declare -- `Api` below is the whole of it, forwarding only the two methods
6//! this program actually uses.
7//!
8//! ```sh
9//! cargo run --features nusb --example backends
10//! cargo run --features nusb --example backends -- 046d c216
11//! ```
12
13use std::env;
14
15use hidra::{DeviceInfo, HidResult, Hidra, MaybeFuture, Native, Nusb};
16
17enum Api {
18    Native(Hidra<Native>),
19    Nusb(Hidra<Nusb>),
20}
21
22impl Api {
23    fn name(&self) -> &'static str {
24        match self {
25            Api::Native(_) => "native",
26            Api::Nusb(_) => "nusb",
27        }
28    }
29
30    fn device_list(&self) -> Vec<&DeviceInfo> {
31        match self {
32            Api::Native(api) => api.device_list().collect(),
33            Api::Nusb(api) => api.device_list().collect(),
34        }
35    }
36
37    fn product_string_of(&self, vid: u16, pid: u16) -> HidResult<Option<String>> {
38        match self {
39            Api::Native(api) => api.open(vid, pid).wait()?.get_product_string().wait(),
40            Api::Nusb(api) => api.open(vid, pid).wait()?.get_product_string().wait(),
41        }
42    }
43}
44
45fn main() -> HidResult<()> {
46    let mut args = env::args().skip(1);
47    let target = match (args.next(), args.next()) {
48        (Some(vid), Some(pid)) => Some((
49            u16::from_str_radix(&vid, 16).expect("vid must be hex"),
50            u16::from_str_radix(&pid, 16).expect("pid must be hex"),
51        )),
52        _ => None,
53    };
54
55    let mut apis = Vec::new();
56    match Hidra::<Native>::builder().build() {
57        Ok(api) => apis.push(Api::Native(api)),
58        Err(e) => println!("native: {e}"),
59    }
60    match Hidra::<Nusb>::builder().build() {
61        Ok(api) => apis.push(Api::Nusb(api)),
62        Err(e) => println!("nusb: {e}"),
63    }
64
65    for api in &apis {
66        let count = api
67            .device_list()
68            .into_iter()
69            .filter(|d| {
70                target.is_none_or(|(vid, pid)| d.vendor_id() == vid && d.product_id() == pid)
71            })
72            .count();
73        println!("{}: {count} device(s)", api.name());
74    }
75
76    let Some((vid, pid)) = target else {
77        return Ok(());
78    };
79
80    for api in &apis {
81        match api.product_string_of(vid, pid) {
82            Ok(product) => {
83                println!("opened {vid:04x}:{pid:04x} on {}", api.name());
84                println!("product: {product:?}");
85                return Ok(());
86            }
87            Err(e) => println!("{}: {e}", api.name()),
88        }
89    }
90    println!("{vid:04x}:{pid:04x} not reachable through either backend");
91    Ok(())
92}