get device names
This commit is contained in:
parent
14365ec9be
commit
9f5c2fcabb
|
@ -5,6 +5,7 @@ authors = ["Magnus Ulimoen <magnus@ulimoen.dev>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
sane-sys = { path = "sane-sys" }
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
|
|
136
src/main.rs
136
src/main.rs
|
@ -1,3 +1,135 @@
|
||||||
fn main() {
|
use sane_sys::*;
|
||||||
println!("Hello, world!");
|
use std::ffi::CStr;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
enum Error {
|
||||||
|
Status(SANE_Status),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
match *self {
|
||||||
|
Error::Status(status) => match status {
|
||||||
|
SANE_Status_SANE_STATUS_GOOD => write!(f, "No error"),
|
||||||
|
SANE_Status_SANE_STATUS_UNSUPPORTED => write!(f, "Unsupported"),
|
||||||
|
SANE_Status_SANE_STATUS_CANCELLED => write!(f, "Cancelled"),
|
||||||
|
SANE_Status_SANE_STATUS_DEVICE_BUSY => write!(f, "Device busy"),
|
||||||
|
SANE_Status_SANE_STATUS_INVAL => write!(f, "Invalid value"),
|
||||||
|
SANE_Status_SANE_STATUS_EOF => write!(f, "End of file"),
|
||||||
|
SANE_Status_SANE_STATUS_JAMMED => write!(f, "Document feeder is jammed"),
|
||||||
|
SANE_Status_SANE_STATUS_NO_DOCS => write!(f, "Document feed is empty"),
|
||||||
|
SANE_Status_SANE_STATUS_COVER_OPEN => write!(f, "Cover is open"),
|
||||||
|
SANE_Status_SANE_STATUS_IO_ERROR => write!(f, "Device IO failed"),
|
||||||
|
SANE_Status_SANE_STATUS_NO_MEM => write!(f, "Not enough memory available"),
|
||||||
|
SANE_Status_SANE_STATUS_ACCESS_DENIED => write!(f, "Access denied"),
|
||||||
|
_ => write!(f, "UNKNOWN ERROR: {}", status),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {}
|
||||||
|
|
||||||
|
fn checked(f: impl FnOnce() -> SANE_Status) -> Result<(), Error> {
|
||||||
|
let status = f();
|
||||||
|
if status != SANE_Status_SANE_STATUS_GOOD {
|
||||||
|
Err(Error::Status(status))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Must be kept active during the scan session
|
||||||
|
struct Context {}
|
||||||
|
impl Context {
|
||||||
|
fn init() -> Result<(Self, Version), Error> {
|
||||||
|
let mut version_code = -1;
|
||||||
|
unsafe {
|
||||||
|
checked(|| sane_init(&mut version_code, None))?;
|
||||||
|
};
|
||||||
|
Ok((Context {}, Version(version_code)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn devices(&self, only_local: bool) -> Result<impl Iterator<Item = Device>, Error> {
|
||||||
|
let mut device_list: *mut *const SANE_Device = std::ptr::null_mut();
|
||||||
|
unsafe {
|
||||||
|
checked(|| sane_get_devices(&mut device_list, only_local as _))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut num_devices = 0;
|
||||||
|
unsafe {
|
||||||
|
let mut traveller = device_list;
|
||||||
|
while !(*traveller).is_null() {
|
||||||
|
traveller = traveller.offset(1);
|
||||||
|
num_devices += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("{}", num_devices);
|
||||||
|
|
||||||
|
Ok((0..num_devices).map(move |i| {
|
||||||
|
let device = unsafe { *device_list.offset(i) };
|
||||||
|
Device(device)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Device(*const SANE_Device);
|
||||||
|
|
||||||
|
impl Device {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
let cstr = unsafe { CStr::from_ptr((*self.0).name) };
|
||||||
|
cstr.to_str().unwrap()
|
||||||
|
}
|
||||||
|
fn vendor(&self) -> &str {
|
||||||
|
let cstr = unsafe { CStr::from_ptr((*self.0).vendor) };
|
||||||
|
cstr.to_str().unwrap()
|
||||||
|
}
|
||||||
|
fn model(&self) -> &str {
|
||||||
|
let cstr = unsafe { CStr::from_ptr((*self.0).model) };
|
||||||
|
cstr.to_str().unwrap()
|
||||||
|
}
|
||||||
|
fn type_(&self) -> &str {
|
||||||
|
let cstr = unsafe { CStr::from_ptr((*self.0).type_) };
|
||||||
|
cstr.to_str().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Context {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { sane_exit() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
struct Version(SANE_Int);
|
||||||
|
|
||||||
|
impl Version {
|
||||||
|
fn major(self) -> SANE_Word {
|
||||||
|
SANE_VERSION_MAJOR(self.0)
|
||||||
|
}
|
||||||
|
fn minor(self) -> SANE_Word {
|
||||||
|
SANE_VERSION_MINOR(self.0)
|
||||||
|
}
|
||||||
|
fn build(self) -> SANE_Word {
|
||||||
|
SANE_VERSION_BUILD(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let (context, version) = Context::init().unwrap();
|
||||||
|
println!(
|
||||||
|
"Version: major: {} minor: {} build: {}",
|
||||||
|
version.major(),
|
||||||
|
version.minor(),
|
||||||
|
version.build()
|
||||||
|
);
|
||||||
|
for device in context.devices(true).unwrap() {
|
||||||
|
println!("Device:");
|
||||||
|
println!("\tname: {}", device.name());
|
||||||
|
println!("\tvendor: {}", device.vendor());
|
||||||
|
println!("\tmodel: {}", device.model());
|
||||||
|
println!("\ttype: {}", device.type_());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue