use super::DecoderConfig;
use crate::SymbolType;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub(crate) struct DecoderState {
symbologies: HashMap<SymbolType, SymbologyConfig>,
pub(crate) scanner: ScannerConfig,
}
#[derive(Debug, Clone, Default)]
pub(crate) struct SymbologyConfig {
pub(crate) enabled: bool,
pub(crate) checksum: ChecksumConfig,
pub(crate) length_limits: Option<LengthLimits>,
pub(crate) uncertainty: u32,
}
#[derive(Debug, Clone, Copy, Default)]
pub(crate) struct ChecksumConfig {
pub(crate) add_check: bool,
pub(crate) emit_check: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct LengthLimits {
pub(crate) min: u32,
pub(crate) max: u32,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct ScannerConfig {
pub(crate) position_tracking: bool,
pub(crate) test_inverted: bool,
pub(crate) x_density: u32,
pub(crate) y_density: u32,
}
impl Default for ScannerConfig {
fn default() -> Self {
Self {
position_tracking: true,
test_inverted: false,
x_density: 1,
y_density: 1,
}
}
}
impl Default for DecoderState {
fn default() -> Self {
(&DecoderConfig::new()).into()
}
}
impl From<&DecoderConfig> for DecoderState {
fn from(config: &DecoderConfig) -> Self {
let mut symbologies = HashMap::new();
for &sym in &config.enabled {
let mut sym_config = SymbologyConfig {
enabled: true,
..Default::default()
};
if let Some(&(add, emit)) = config.checksum_flags.get(&sym) {
sym_config.checksum = ChecksumConfig {
add_check: add,
emit_check: emit,
};
}
if let Some(&(min, max)) = config.length_limits.get(&sym) {
sym_config.length_limits = Some(LengthLimits { min, max });
}
if let Some(&threshold) = config.uncertainty.get(&sym) {
sym_config.uncertainty = threshold;
}
symbologies.insert(sym, sym_config);
}
for &sym in SymbolType::ALL.iter() {
if !config.enabled.contains(&sym) {
let mut sym_config = SymbologyConfig {
enabled: false,
..Default::default()
};
let has_config = config.checksum_flags.contains_key(&sym)
|| config.length_limits.contains_key(&sym)
|| config.uncertainty.contains_key(&sym);
if has_config {
if let Some(&(add, emit)) = config.checksum_flags.get(&sym) {
sym_config.checksum = ChecksumConfig {
add_check: add,
emit_check: emit,
};
}
if let Some(&(min, max)) = config.length_limits.get(&sym) {
sym_config.length_limits = Some(LengthLimits { min, max });
}
if let Some(&threshold) = config.uncertainty.get(&sym) {
sym_config.uncertainty = threshold;
}
symbologies.insert(sym, sym_config);
}
}
}
Self {
symbologies,
scanner: ScannerConfig {
position_tracking: config.position_tracking,
test_inverted: config.test_inverted,
x_density: config.x_density,
y_density: config.y_density,
},
}
}
}
impl DecoderState {
pub(crate) fn is_enabled(&self, sym: SymbolType) -> bool {
self.symbologies
.get(&sym)
.map(|c| c.enabled)
.unwrap_or(false)
}
pub(crate) fn get(&self, sym: SymbolType) -> Option<&SymbologyConfig> {
self.symbologies.get(&sym)
}
pub(crate) fn ean_enabled(&self) -> bool {
[
SymbolType::Ean2,
SymbolType::Ean5,
SymbolType::Ean8,
SymbolType::Ean13,
SymbolType::Upca,
SymbolType::Upce,
SymbolType::Isbn10,
SymbolType::Isbn13,
]
.iter()
.any(|&sym| self.is_enabled(sym))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_state() {
let state = DecoderState::default();
assert!(state.is_enabled(SymbolType::Ean13));
assert!(state.is_enabled(SymbolType::Code39));
assert!(state.scanner.position_tracking);
assert_eq!(state.scanner.x_density, 1);
assert_eq!(state.scanner.y_density, 1);
}
#[test]
fn test_symbology_config() {
use crate::config::{Code39, DecoderConfig};
let config = DecoderConfig::new()
.enable(Code39)
.set_length_limits(Code39, 4, 20)
.set_checksum(Code39, true, false)
.set_uncertainty(Code39, 2);
let state: DecoderState = (&config).into();
let code39_config = state.get(SymbolType::Code39).unwrap();
assert!(code39_config.enabled);
assert_eq!(
code39_config.length_limits,
Some(LengthLimits { min: 4, max: 20 })
);
assert!(code39_config.checksum.add_check);
assert!(!code39_config.checksum.emit_check);
assert_eq!(code39_config.uncertainty, 2);
}
#[test]
fn test_ean_enabled() {
let state = DecoderState::default();
assert!(state.ean_enabled());
let mut symbologies = std::collections::HashMap::new();
symbologies.insert(
SymbolType::Code39,
SymbologyConfig {
enabled: true,
..Default::default()
},
);
symbologies.insert(
SymbolType::Code128,
SymbologyConfig {
enabled: true,
..Default::default()
},
);
let state = DecoderState {
symbologies,
scanner: ScannerConfig::default(),
};
assert!(!state.ean_enabled());
let mut symbologies = std::collections::HashMap::new();
symbologies.insert(
SymbolType::Ean13,
SymbologyConfig {
enabled: true,
..Default::default()
},
);
let state = DecoderState {
symbologies,
scanner: ScannerConfig::default(),
};
assert!(state.ean_enabled());
}
#[test]
fn test_scanner_config() {
let config = DecoderConfig::new()
.position_tracking(false)
.test_inverted(true)
.scan_density(2, 3);
let state: DecoderState = (&config).into();
assert!(!state.scanner.position_tracking);
assert!(state.scanner.test_inverted);
assert_eq!(state.scanner.x_density, 2);
assert_eq!(state.scanner.y_density, 3);
}
}