wdi_rs/
lib.rs

1// Copyright (C) 2025 Piers Finlayson <piers@piers.rocks>
2//
3// MIT License
4
5//! # wdi-rs - Windows Driver Installer for Rust
6//!
7//! This crate provides a Rust API to [libwdi](https://github.com/pbatard/libwdi), the library behind [Zadiq](https://zadig.akeo.ie/), which provides simple Windows device driver installation.  It is particularly useful for installing the WinUSB drivers for USB devices, allowing user-mode applications to communicate with them without needing to write a custom driver.
8//!
9//! As well as exposing the libwdi primitives, this crate exposes a higher level [`DriverInstaller`] builder API which simplifies common use cases such as:
10//! - installing a driver for a specific device by VID/PID
11//! - enumerating devices and selecting one based on custom criteria
12//! - using a custom INF file for driver installation, allowing more flexibility than the stock libwdi APIs.
13//!
14//! This is a Windows specific crate, and currently only targets x86 64-bit.
15//!
16//! ## Features
17//!
18//! - 🚀 High-level builder API for driver installation
19//! - 🔍 USB device enumeration and discovery
20//! - 📝 Custom INF file support (embedded or external)
21//! - 🛡️ Type-safe bindings to libwdi
22//! - 📋 Comprehensive logging support
23//! - ✅ x86 64-bit support
24//!
25//! ## Installation
26//!
27//! Add this to your `Cargo.toml`:
28//!
29//! ```toml
30//! [dependencies]
31//! wdi-rs = "0.1"
32//! ```
33//!
34//! ## Quick Start
35//!
36//! ### Install WinUSB driver for a specific device
37//!
38//! The simplest use case - install WinUSB for a device by its VID/PID:
39//!
40//! ```no_run
41//! use wdi_rs::DriverInstaller;
42//!
43//! fn main() -> Result<(), wdi_rs::Error> {
44//!     // Install WinUSB driver for device 1234:5678
45//!     // libwdi will automatically generate an appropriate INF file
46//!     DriverInstaller::for_device(0x1234, 0x5678)
47//!         .install()?;
48//!     
49//!     println!("Driver installed successfully!");
50//!     Ok(())
51//! }
52//! ```
53//!
54//! ### Using a custom INF file
55//!
56//! For production use, you'll typically want to provide your own INF file, allowing you to custom more details of the driver installation:
57//!
58//! ```no_run
59//! use wdi_rs::DriverInstaller;
60//!
61//! // Embed your INF file at compile time
62//! const MY_DEVICE_INF: &[u8] = include_bytes!("..\\inf\\sample.inf");
63//!
64//! fn main() -> Result<(), wdi_rs::Error> {
65//!     DriverInstaller::for_device(0x1234, 0x5678)
66//!         .with_inf_data(MY_DEVICE_INF, "..\\inf\\sample.inf")
67//!         .install()?;
68//!     
69//!     Ok(())
70//! }
71//! ```
72//!
73//! ### Enumerate devices before installation
74//!
75//! If you need to discover or select devices interactively:
76//!
77//! ```no_run
78//! use wdi_rs::{create_list, CreateListOptions, DriverInstaller};
79//!
80//! fn main() -> Result<(), wdi_rs::Error> {
81//!     // Enumerate all USB devices
82//!     let devices = create_list(CreateListOptions::default())?;
83//!     
84//!     // Find your specific device
85//!     let device = devices.iter()
86//!         .find(|d| d.vid == 0x1234 && d.pid == 0x5678)
87//!         .ok_or(wdi_rs::Error::NotFound)?;
88//!     
89//!     println!("Found device: {}", device);
90//!     
91//!     // Install driver for this specific device
92//!     DriverInstaller::for_specific_device(device.clone())
93//!         .install()?;
94//!     
95//!     Ok(())
96//! }
97//! ```
98//!
99//! ### Advanced: Custom device selection
100//!
101//! For more complex device selection logic:
102//!
103//! ```no_run
104//! use wdi_rs::{DriverInstaller, DeviceSelector};
105//!
106//! fn main() -> Result<(), wdi_rs::Error> {
107//!     // Install driver for the first device matching a custom predicate
108//!     DriverInstaller::new(DeviceSelector::First(Box::new(|dev| {
109//!         dev.vid == 0x1234 && 
110//!         dev.desc.as_ref()
111//!             .map_or(false, |desc| desc.contains("My Custom Device"))
112//!     })))
113//!     .install()?;
114//!     
115//!     Ok(())
116//! }
117//! ```
118//!
119//! ### Complete example with error handling and logging
120//!
121//! ```no_run
122//! use wdi_rs::{DriverInstaller, DriverType, Error};
123//! use log::{info, error};
124//!
125//! const MY_DEVICE_INF: &[u8] = include_bytes!("..\\inf\\sample.inf");
126//!
127//! fn install_driver(vid: u16, pid: u16) -> Result<(), Error> {
128//!     info!("Starting driver installation for {:04x}:{:04x}", vid, pid);
129//!     
130//!     match DriverInstaller::for_device(vid, pid)
131//!         .with_inf_data(MY_DEVICE_INF, "..\\inf\\sample.inf")
132//!         .with_driver_type(DriverType::WinUsb)
133//!         .install()
134//!     {
135//!         Ok(device) => {
136//!             info!("Successfully installed driver for: {}", device);
137//!             Ok(())
138//!         }
139//!         Err(Error::Exists) => {
140//!             info!("Driver already installed");
141//!             Ok(())
142//!         }
143//!         Err(Error::NotFound) => {
144//!             error!("Device {:04x}:{:04x} not found", vid, pid);
145//!             Err(Error::NotFound)
146//!         }
147//!         Err(e) => {
148//!             error!("Failed to install driver: {}", e);
149//!             Err(e)
150//!         }
151//!     }
152//! }
153//!
154//! fn main() {
155//!     env_logger::init();
156//!     
157//!     if let Err(e) = install_driver(0x1234, 0x5678) {
158//!         eprintln!("Installation failed: {}", e);
159//!         std::process::exit(1);
160//!     }
161//! }
162//! ```
163//!
164//! ## Low-Level API
165//!
166//! For cases where you need direct control, wdi-rs also exposes the low-level libwdi functions:
167//!
168//! ```no_run
169//! use wdi_rs::{create_list, prepare_driver, install_driver, CreateListOptions, 
170//!           PrepareDriverOptions, InstallDriverOptions, DriverType};
171//!
172//! fn main() -> Result<(), wdi_rs::Error> {
173//!     // Get device list
174//!     let devices = create_list(CreateListOptions {
175//!         list_all: true,
176//!         list_hubs: false,
177//!         trim_whitespaces: true,
178//!     })?;
179//!     
180//!     let device = &devices.get(0).ok_or(wdi_rs::Error::NotFound)?;
181//!     
182//!     // Prepare driver
183//!     let mut prepare_opts = PrepareDriverOptions::default();
184//!     prepare_opts.driver_type = DriverType::WinUsb;
185//!     
186//!     prepare_driver(
187//!         device,
188//!         "C:\\drivers",
189//!         "C:\\drivers\\device.inf",
190//!         &prepare_opts,
191//!     )?;
192//!     
193//!     // Install driver
194//!     install_driver(
195//!         device,
196//!         "C:\\drivers",
197//!         "C:\\drivers\\device.inf",
198//!         &InstallDriverOptions::default(),
199//!     )?;
200//!     
201//!     Ok(())
202//! }
203//! ```
204//!
205//! ## Platform Support
206//!
207//! - **Windows 7+** (x64)
208//! - Requires administrator privileges for driver installation
209//! - This crate only compiles on Windows
210//!
211//! ## Architecture
212//!
213//! wdi-rs consists of two layers:
214//!
215//! 1. **Low-level FFI bindings** to libwdi (`prepare_driver`, `install_driver`, etc.)
216//! 2. **High-level API** with `DriverInstaller` builder pattern
217//!
218//! The high-level API is recommended for most use cases as it handles:
219//! - Device enumeration and selection
220//! - Temporary file management
221//! - INF file handling
222//! - Error propagation
223//! - Safe cleanup
224//!
225//! ## Common Use Cases
226//!
227//! ### Command-line tool for driver installation
228//!
229//! ```no_run
230//! use wdi_rs::DriverInstaller;
231//! use std::env;
232//!
233//! fn main() {
234//!     let args: Vec<String> = env::args().collect();
235//!     if args.len() != 3 {
236//!         eprintln!("Usage: {} <VID> <PID>", args[0]);
237//!         std::process::exit(1);
238//!     }
239//!     
240//!     let vid = u16::from_str_radix(&args[1], 16).unwrap();
241//!     let pid = u16::from_str_radix(&args[2], 16).unwrap();
242//!     
243//!     DriverInstaller::for_device(vid, pid)
244//!         .install()
245//!         .expect("Failed to install driver");
246//! }
247//! ```
248//!
249//! ### GUI application with device selection
250//!
251//! ```no_run
252//! use wdi_rs::{create_list, CreateListOptions, DriverInstaller};
253//!
254//! fn list_devices() -> Result<Vec<String>, wdi_rs::Error> {
255//!     let devices = create_list(CreateListOptions::default())?;
256//!     Ok(devices.iter()
257//!         .map(|d| format!("{:04x}:{:04x} - {}", d.vid, d.pid, 
258//!                          d.desc.as_deref().unwrap_or("Unknown")))
259//!         .collect())
260//! }
261//!
262//! fn install_for_selected(vid: u16, pid: u16) -> Result<(), wdi_rs::Error> {
263//!     DriverInstaller::for_device(vid, pid).install()?;
264//!     Ok(())
265//! }
266//! ```
267//!
268//! ### Installer package
269//!
270//! Embed wdi-rs in your application's installer to automatically set up USB drivers:
271//!
272//! ```no_run
273//! use wdi_rs::DriverInstaller;
274//!
275//! const DEVICE_INF: &[u8] = include_bytes!("..\\inf\\sample.inf");
276//!
277//! fn setup_usb_driver() -> Result<(), Box<dyn std::error::Error>> {
278//!     println!("Setting up USB driver...");
279//!     
280//!     DriverInstaller::for_device(0x1234, 0x5678)
281//!         .with_inf_data(DEVICE_INF, "device.inf")
282//!         .install()?;
283//!     
284//!     println!("USB driver installed successfully!");
285//!     Ok(())
286//! }
287//! ```
288//!
289//! ## Logging
290//!
291//! wdi-rs uses the `log` crate for logging. Enable logging in your application:
292//!
293//! ```no_run
294//! use log::LevelFilter;
295//!
296//! fn main() {
297//!     env_logger::Builder::from_default_env()
298//!         .filter_level(LevelFilter::Info)
299//!         .init();
300//!     
301//!     // Also set libwdi's log level
302//!     wdi_rs::set_log_level(log::max_level().into()).ok();
303//!     
304//!     // Your code here...
305//! }
306//! ```
307//!
308//! ## Safety
309//!
310//! This crate uses unsafe code to interface with the libwdi C library. All unsafe code is carefully reviewed and encapsulated behind safe APIs. The high-level `DriverInstaller` API is entirely safe Rust.
311//!
312//! ## License
313//!
314//! MIT or Apache 2.0 License, at your option - see LICENSE file for details.
315//!
316//! ## Contributing
317//!
318//! Contributions are welcome! Please open an issue or PR on GitHub.
319//!
320//! ## Credits
321//!
322//! - [libwdi](https://github.com/pbatard/libwdi) by Pete Batard - the underlying C library
323//!
324//! ## See Also
325//!
326//! - [libwdi documentation](https://github.com/pbatard/libwdi/wiki)
327//! - [rusb](https://crates.io/crates/rusb) - USB library for Rust
328//! - [nusb](https://crates.io/crates/nusb) - Modern USB library for Rust
329
330#[cfg(any(target_os = "windows", doc))]
331mod ffi;
332#[cfg(any(target_os = "windows", doc))]
333mod installer;
334#[cfg(any(target_os = "windows", doc))]
335mod wdi;
336
337#[cfg(any(target_os = "windows", doc))]
338pub use installer::{DriverInstaller, DeviceSelector, InfSource, InstallOptions};
339#[cfg(any(target_os = "windows", doc))]
340pub use wdi::{
341    create_list, prepare_driver, install_driver,
342    CreateListOptions, Device, DeviceList, PrepareDriverOptions, InstallDriverOptions,
343    DriverType, Error, set_log_level,
344};
345
346#[cfg(all(not(target_os = "windows"), not(doc)))]
347compile_error!("This crate only supports Windows");