use super::Error;
use crate::protocol::model::Model;
pub const SCANNER: u8 = 0x06;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Identity {
pub qualifier: u8,
pub device_type: u8,
pub removable: bool,
pub ansi_version: u8,
pub vendor: String,
pub product: String,
pub revision: String,
}
impl Identity {
pub const LENGTH: usize = 36;
pub fn model(&self) -> Option<Model> {
Model::from_product(&self.product)
}
pub fn is_mf_scanner(&self) -> bool {
matches!(
Model::from_product(&self.product).unwrap().into(),
Some(Model::Ls8000 | Model::Ls9000)
)
}
pub fn parse(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() < Self::LENGTH {
return Err(Error::Truncated {
page: 0,
need: Self::LENGTH,
got: bytes.len(),
});
}
Ok(Self {
qualifier: bytes[0] >> 5,
device_type: bytes[0] & 0x1F,
removable: bytes[1] & 0x80 != 0,
ansi_version: bytes[2] & 0x07,
vendor: ascii(&bytes[8..16]),
product: ascii(&bytes[16..32]),
revision: ascii(&bytes[32..36]),
})
}
pub fn is_scanner(&self) -> bool {
self.qualifier == 0 && self.device_type == SCANNER
}
}
fn ascii(bytes: &[u8]) -> String {
String::from_utf8_lossy(bytes).trim().to_string()
}
#[cfg(test)]
mod tests {
use super::*;
fn ls9000() -> Vec<u8> {
let mut p = vec![b' '; 36];
p[0] = 0x06;
p[8..16].copy_from_slice(b"Nikon ");
p[16..32].copy_from_slice(b"LS-9000 ED ");
p
}
#[test]
fn only_a_type_six_with_a_zero_qualifier_is_a_scanner() {
assert!(Identity::parse(&ls9000()).unwrap().is_scanner());
let mut p = ls9000();
p[0] = 0b011_00110;
assert!(!Identity::parse(&p).unwrap().is_scanner());
p[0] = 0x00; assert!(!Identity::parse(&p).unwrap().is_scanner());
}
#[test]
fn the_identification_fields_are_trimmed() {
let id = Identity::parse(&ls9000()).unwrap();
assert_eq!(id.vendor, "Nikon");
assert_eq!(id.product, "LS-9000 ED");
}
}