1use std::fmt;
43use std::marker::PhantomData;
44use std::os::raw::c_int;
45use std::ptr::NonNull;
46
47use zbar_sys as ffi;
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub enum Error {
52 InvalidArgument,
54 OutOfMemory,
56 InternalError,
58 SystemError,
60 Unsupported,
62 Unknown(i32),
64}
65
66impl fmt::Display for Error {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 match self {
69 Error::InvalidArgument => write!(f, "invalid argument"),
70 Error::OutOfMemory => write!(f, "out of memory"),
71 Error::InternalError => write!(f, "internal error"),
72 Error::SystemError => write!(f, "system error"),
73 Error::Unsupported => write!(f, "unsupported operation"),
74 Error::Unknown(code) => write!(f, "unknown error: {}", code),
75 }
76 }
77}
78
79impl std::error::Error for Error {}
80
81pub type Result<T> = std::result::Result<T, Error>;
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
85#[repr(u32)]
86pub enum SymbolType {
87 None = ffi::ZBAR_NONE,
88 Partial = ffi::ZBAR_PARTIAL,
89 EAN2 = ffi::ZBAR_EAN2,
90 EAN5 = ffi::ZBAR_EAN5,
91 EAN8 = ffi::ZBAR_EAN8,
92 UPCE = ffi::ZBAR_UPCE,
93 ISBN10 = ffi::ZBAR_ISBN10,
94 UPCA = ffi::ZBAR_UPCA,
95 EAN13 = ffi::ZBAR_EAN13,
96 ISBN13 = ffi::ZBAR_ISBN13,
97 COMPOSITE = ffi::ZBAR_COMPOSITE,
98 I25 = ffi::ZBAR_I25,
99 DATABAR = ffi::ZBAR_DATABAR,
100 DatabarExp = ffi::ZBAR_DATABAR_EXP,
101 CODABAR = ffi::ZBAR_CODABAR,
102 CODE39 = ffi::ZBAR_CODE39,
103 PDF417 = ffi::ZBAR_PDF417,
104 QRCODE = ffi::ZBAR_QRCODE,
105 SQCODE = ffi::ZBAR_SQCODE,
106 CODE93 = ffi::ZBAR_CODE93,
107 CODE128 = ffi::ZBAR_CODE128,
108}
109
110pub struct Image {
112 raw: NonNull<ffi::zbar_image_t>,
113 owned: bool,
114}
115
116impl Image {
117 pub fn from_gray(data: &[u8], width: u32, height: u32) -> Result<Self> {
119 unsafe {
120 let img = ffi::zbar_image_create();
121 if img.is_null() {
122 return Err(Error::OutOfMemory);
123 }
124
125 let format =
127 ('Y' as u64) | (('8' as u64) << 8) | (('0' as u64) << 16) | (('0' as u64) << 24);
128 ffi::zbar_image_set_format(img, format);
129 ffi::zbar_image_set_size(img, width, height);
130 ffi::zbar_image_set_data(
131 img,
132 data.as_ptr() as *const _,
133 (width * height) as u64,
134 None,
135 );
136
137 Ok(Image {
138 raw: NonNull::new_unchecked(img),
139 owned: true,
140 })
141 }
142 }
143
144 pub(crate) fn as_ptr(&self) -> *mut ffi::zbar_image_t {
146 self.raw.as_ptr()
147 }
148}
149
150impl Drop for Image {
151 fn drop(&mut self) {
152 if self.owned {
153 unsafe {
154 ffi::zbar_image_destroy(self.raw.as_ptr());
155 }
156 }
157 }
158}
159
160unsafe impl Send for Image {}
161unsafe impl Sync for Image {}
162
163#[derive(Clone)]
165pub struct Symbol<'a> {
166 raw: *const ffi::zbar_symbol_t,
167 _phantom: PhantomData<&'a ()>,
168}
169
170impl<'a> Symbol<'a> {
171 pub fn symbol_type(&self) -> SymbolType {
173 unsafe {
174 let t = ffi::zbar_symbol_get_type(self.raw);
175 std::mem::transmute(t)
176 }
177 }
178
179 pub fn data(&self) -> &str {
181 unsafe {
182 let ptr = ffi::zbar_symbol_get_data(self.raw);
183 let len = ffi::zbar_symbol_get_data_length(self.raw) as usize;
184 let slice = std::slice::from_raw_parts(ptr as *const u8, len);
185 std::str::from_utf8_unchecked(slice)
186 }
187 }
188
189 pub fn quality(&self) -> i32 {
191 unsafe { ffi::zbar_symbol_get_quality(self.raw) }
192 }
193}
194
195pub struct SymbolIter<'a> {
197 current: *const ffi::zbar_symbol_t,
198 _phantom: PhantomData<&'a ()>,
199}
200
201impl<'a> Iterator for SymbolIter<'a> {
202 type Item = Symbol<'a>;
203
204 fn next(&mut self) -> Option<Self::Item> {
205 if self.current.is_null() {
206 return None;
207 }
208
209 let symbol = Symbol {
210 raw: self.current,
211 _phantom: PhantomData,
212 };
213
214 unsafe {
215 self.current = ffi::zbar_symbol_next(self.current);
216 }
217
218 Some(symbol)
219 }
220}
221
222pub struct ImageScanner {
224 raw: NonNull<ffi::zbar_image_scanner_t>,
225}
226
227impl ImageScanner {
228 pub fn new() -> Result<Self> {
230 unsafe {
231 let scanner = ffi::zbar_image_scanner_create();
232 if scanner.is_null() {
233 return Err(Error::OutOfMemory);
234 }
235
236 Ok(ImageScanner {
237 raw: NonNull::new_unchecked(scanner),
238 })
239 }
240 }
241
242 pub fn set_config(&mut self, symbol_type: SymbolType, config: u32, value: c_int) -> Result<()> {
244 unsafe {
245 let ret = ffi::zbar_image_scanner_set_config(
246 self.raw.as_ptr(),
247 symbol_type as u32,
248 config,
249 value,
250 );
251 if ret != 0 {
252 return Err(Error::InvalidArgument);
253 }
254 Ok(())
255 }
256 }
257
258 pub fn scan_image<'a>(&mut self, image: &'a Image) -> Result<SymbolIter<'a>> {
260 unsafe {
261 let n = ffi::zbar_scan_image(self.raw.as_ptr(), image.as_ptr());
262 if n < 0 {
263 return Err(Error::InternalError);
264 }
265
266 let first_symbol = ffi::zbar_image_first_symbol(image.as_ptr());
267
268 Ok(SymbolIter {
269 current: first_symbol,
270 _phantom: PhantomData,
271 })
272 }
273 }
274}
275
276impl Default for ImageScanner {
277 fn default() -> Self {
278 Self::new().expect("failed to create image scanner")
279 }
280}
281
282impl Drop for ImageScanner {
283 fn drop(&mut self) {
284 unsafe {
285 ffi::zbar_image_scanner_destroy(self.raw.as_ptr());
286 }
287 }
288}
289
290unsafe impl Send for ImageScanner {}
291unsafe impl Sync for ImageScanner {}
292
293pub fn version() -> (u32, u32) {
295 unsafe {
296 let mut major = 0u32;
297 let mut minor = 0u32;
298 ffi::zbar_version(&mut major, &mut minor, std::ptr::null_mut());
299 (major, minor)
300 }
301}
302
303pub fn set_verbosity(verbosity: i32) {
320 unsafe {
321 ffi::zbar_set_verbosity(verbosity);
322 }
323}
324
325#[cfg(test)]
326mod tests {
327 use super::*;
328
329 #[test]
330 fn test_version() {
331 let (major, minor) = version();
332 assert_eq!(major, 0, "ZBar major version should be 0");
333 assert_eq!(minor, 23, "ZBar minor version should be 23");
334 println!("ZBar version: {}.{}", major, minor);
335 }
336
337 #[test]
338 fn test_scanner_creation() {
339 let scanner = ImageScanner::new();
340 assert!(scanner.is_ok(), "Failed to create scanner");
341 }
342}