barcode_scanner/
lib.rs

1//! Scan 1D barcodes using a hand scanner for Rust.
2//!
3//! The `barcode-scanner` crate provides an interface to USB barcode scanners on Linux.
4//! It is built on top of the [`evdev`] crate.
5//! It works with any barcode scanner that acts as a keyboard.
6//!
7//! Currently supported features:
8//! * One [`BarcodeScanner`] struct for all USB hand scanners that operate as a keyboard.
9//! * Prevent other clients from receiving events from the selected device by grabbing it.
10//! * Read 1D barcode consisting of numbers and letters.
11//! * Omit special characters in a barcode.
12//!
13//! # Example
14//! This example grabs a hand scanner and prints a barcode that is read.
15//!
16//! ```no_run
17//! # fn example() -> Result<(), barcode_scanner::Error> {
18//!    use barcode_scanner::BarcodeScanner;
19//!
20//!    let mut scanner = BarcodeScanner::open("/dev/input/by-id/usb-USB_Adapter_USB_Device-event-kbd")?;
21//!    loop {
22//!        let barcode = scanner.read()?;
23//!        println!("{}", barcode);
24//!    }
25//! # }
26//! ```
27
28use std::path::Path;
29
30/// A barcode scanner.
31pub struct BarcodeScanner {
32	/// The underlying evdev device.
33	device: evdev::Device,
34
35	/// A buffer used to collect keystrokes in until a whole barcode has been read.
36	buffer: String,
37}
38
39/// An error reported by the barcode scanner.
40#[derive(Debug, Clone)]
41pub struct Error {
42	msg: String,
43}
44
45impl BarcodeScanner {
46	/// Create a barcode scanner and grab the device by a device path
47	///
48	/// # Example
49	/// ```no_run
50	/// # use barcode_scanner::BarcodeScanner;
51	/// # fn foo() -> Result<(), barcode_scanner::Error> {
52	/// let mut scanner = BarcodeScanner::open("/dev/input/event18")?;
53	/// # Ok(())
54	/// # }
55	/// ```
56	pub fn open(path: impl AsRef<Path>) -> Result<Self, Error> {
57		let path = path.as_ref();
58		let mut device = evdev::Device::open(path)
59			.map_err(|e| Error::new(format!("Failed to open input device {}: {e}", path.display())))?;
60		device.grab()
61			.map_err(|e| Error::new(format!("Failed to grab input device {}: {e}", path.display())))?;
62
63		Ok(Self {
64			device,
65			buffer: String::new(),
66		})
67	}
68
69	/// Create a barcode scanner and grab the device by a physical device path
70	///
71	/// # Example
72	/// ```no_run
73	/// # use barcode_scanner::BarcodeScanner;
74	/// # fn foo() -> Result<(), ()> {
75	/// let device_path = "usb-0000:00:14.0-3/input0";
76	/// let mut scanner = BarcodeScanner::open_by_physical_path(device_path)
77	///     .map_err(|e| eprintln!("{}", e))?
78	///     .ok_or_else(|| eprintln!("No such device: {device_path}"))?;
79	/// # Ok(())
80	/// # }
81	/// ```
82	pub fn open_by_physical_path(physical_path: impl AsRef<str>) -> Result<Option<Self>, Error> {
83		let physical_path = physical_path.as_ref();
84		for (_path, mut device) in evdev::enumerate() {
85			// Find the scanner among other USB devices by physical path.
86			let device_physical_path = match device.physical_path() {
87				Some(x) => x,
88				None => continue,
89			};
90			if device_physical_path == physical_path {
91				// Prevents other clients from receiving events from this device.
92				device.grab()
93					.map_err(|e| Error::new(format!("Failed to grab input device {physical_path}: {e}")))?;
94				return Ok(Some(Self {
95					device,
96					buffer: String::new(),
97				}))
98			}
99		}
100		Ok(None)
101	}
102
103	/// Read a barcode from the device.
104	///
105	/// Blocks until an entire barcode has been read.
106	///
107	/// # Example
108	/// ```no_run
109	/// # use barcode_scanner::BarcodeScanner;
110	/// # fn foo() -> Result<(), barcode_scanner::Error> {
111	/// # let mut scanner = BarcodeScanner::open("/dev/input/event18")?;
112	/// let barcode = scanner.read()?;
113	/// println!("Barcode: {barcode}");
114	/// # Ok(())
115	/// # }
116	pub fn read(&mut self) -> Result<String, Error> {
117		loop {
118			let events = self.device.fetch_events()
119				.map_err(|e| Error::new(format!("Failed to fetch events from input device: {e}")))?;
120			for event in events {
121				// Check if key is pressed (value 1 for the key pressed, velue 0 for the key released).
122				if event.event_type() == evdev::EventType::KEY && event.value() == 1 {
123					// Create Key object based on the code.
124					let key_name = evdev::Key(event.code());
125
126					// Map key_name to the number or char.
127					if let Some(c) = key_to_str(key_name) {
128						self.buffer.push(c);
129					}
130				}
131			}
132
133			if let Some(index) = self.buffer.find('\n') {
134				let mut barcode: String= self.buffer.drain(..index + 1).collect();
135				barcode.pop();
136				return Ok(barcode);
137			}
138		}
139	}
140
141	/// Convert the device into a asynchonous stream of read barcodes.
142	#[cfg(feature = "tokio")]
143	pub fn into_async_stream(mut self) -> tokio::sync::mpsc::UnboundedReceiver<Result<String, Error>> {
144		let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
145		tokio::task::spawn_blocking(move || {
146			loop {
147				if tx.send(self.read()).is_err() {
148					break;
149				}
150			}
151		});
152		rx
153	}
154}
155
156/// Map a scanned key to a character
157fn key_to_str(key: evdev::Key) -> Option<char> {
158	match key {
159		evdev::Key::KEY_A => Some('A'),
160		evdev::Key::KEY_B => Some('B'),
161		evdev::Key::KEY_C => Some('C'),
162		evdev::Key::KEY_D => Some('D'),
163		evdev::Key::KEY_E => Some('E'),
164		evdev::Key::KEY_F => Some('F'),
165		evdev::Key::KEY_G => Some('G'),
166		evdev::Key::KEY_H => Some('H'),
167		evdev::Key::KEY_I => Some('I'),
168		evdev::Key::KEY_J => Some('J'),
169		evdev::Key::KEY_K => Some('K'),
170		evdev::Key::KEY_L => Some('L'),
171		evdev::Key::KEY_M => Some('M'),
172		evdev::Key::KEY_N => Some('N'),
173		evdev::Key::KEY_O => Some('O'),
174		evdev::Key::KEY_P => Some('P'),
175		evdev::Key::KEY_Q => Some('Q'),
176		evdev::Key::KEY_R => Some('R'),
177		evdev::Key::KEY_S => Some('S'),
178		evdev::Key::KEY_T => Some('T'),
179		evdev::Key::KEY_U => Some('U'),
180		evdev::Key::KEY_V => Some('V'),
181		evdev::Key::KEY_W => Some('W'),
182		evdev::Key::KEY_X => Some('X'),
183		evdev::Key::KEY_Y => Some('Y'),
184		evdev::Key::KEY_Z => Some('Z'),
185		evdev::Key::KEY_0 => Some('0'),
186		evdev::Key::KEY_1 => Some('1'),
187		evdev::Key::KEY_2 => Some('2'),
188		evdev::Key::KEY_3 => Some('3'),
189		evdev::Key::KEY_4 => Some('4'),
190		evdev::Key::KEY_5 => Some('5'),
191		evdev::Key::KEY_6 => Some('6'),
192		evdev::Key::KEY_7 => Some('7'),
193		evdev::Key::KEY_8 => Some('8'),
194		evdev::Key::KEY_9 => Some('9'),
195		evdev::Key::KEY_ENTER => Some('\n'),
196		evdev::Key::KEY_KPENTER => Some('\n'),
197		_ => None,
198	}
199}
200
201impl Error {
202	fn new(msg: String) -> Self {
203		Self { msg }
204	}
205}
206
207impl std::fmt::Display for Error {
208	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209		f.write_str(&self.msg)
210	}
211}
212
213impl std::error::Error for Error { }