#[cfg(feature = "code128")]
pub mod code128;
#[cfg(feature = "qr")]
pub mod qr;
#[cfg(feature = "code128")]
pub use code128::Code128;
#[cfg(feature = "qr")]
pub use qr::{Ecc, Qr, QrVersion};
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
use core::fmt;
use crate::error::Result;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum SymbologyKind {
Code128,
Qr,
}
impl SymbologyKind {
pub fn name(self) -> &'static str {
match self {
Self::Code128 => "Code 128",
Self::Qr => "QR Code",
}
}
pub fn is_linear(self) -> bool {
match self {
Self::Code128 => true,
Self::Qr => false,
}
}
pub fn linear_character(self) -> Option<LinearCharacter> {
match self {
Self::Code128 => Some(LinearCharacter {
elements: 6,
modules: 11,
stop_elements: 7,
stop_modules: 13,
}),
Self::Qr => None,
}
}
pub fn required_quiet_zone(self) -> u32 {
match self {
Self::Code128 => 10,
Self::Qr => 4,
}
}
}
impl fmt::Display for SymbologyKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct LinearCharacter {
pub elements: u32,
pub modules: u32,
pub stop_elements: u32,
pub stop_modules: u32,
}
#[derive(Clone, PartialEq, Eq)]
pub struct BitMatrix {
width: u32,
height: u32,
bits: Vec<bool>,
}
impl BitMatrix {
pub fn new(width: u32, height: u32) -> Self {
assert!(
width > 0 && height > 0,
"a symbol must have a positive size"
);
Self {
width,
height,
bits: vec![false; (width as usize) * (height as usize)],
}
}
pub fn from_row(row: Vec<bool>) -> Self {
assert!(!row.is_empty(), "a symbol must have a positive size");
Self {
width: row.len() as u32,
height: 1,
bits: row,
}
}
pub fn from_vec(width: u32, height: u32, bits: Vec<bool>) -> Option<Self> {
if width == 0 || height == 0 {
return None;
}
if bits.len() != (width as usize).checked_mul(height as usize)? {
return None;
}
Some(Self {
width,
height,
bits,
})
}
pub fn width(&self) -> u32 {
self.width
}
pub fn height(&self) -> u32 {
self.height
}
pub fn get(&self, x: u32, y: u32) -> bool {
if x >= self.width || y >= self.height {
return false;
}
self.bits[(y as usize) * (self.width as usize) + (x as usize)]
}
pub fn set(&mut self, x: u32, y: u32, dark: bool) {
if x >= self.width || y >= self.height {
return;
}
let w = self.width as usize;
self.bits[(y as usize) * w + (x as usize)] = dark;
}
pub fn row(&self, y: u32) -> &[bool] {
if y >= self.height {
return &[];
}
let w = self.width as usize;
let start = (y as usize) * w;
&self.bits[start..start + w]
}
}
impl fmt::Debug for BitMatrix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "BitMatrix {}x{}", self.width, self.height)?;
for y in 0..self.height {
for &dark in self.row(y) {
f.write_str(if dark { "#" } else { "." })?;
}
writeln!(f)?;
}
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Symbol {
kind: SymbologyKind,
modules: BitMatrix,
payload: String,
}
impl Symbol {
pub fn new(kind: SymbologyKind, modules: BitMatrix, payload: String) -> Self {
Self {
kind,
modules,
payload,
}
}
pub fn kind(&self) -> SymbologyKind {
self.kind
}
pub fn modules(&self) -> &BitMatrix {
&self.modules
}
pub fn payload(&self) -> &str {
&self.payload
}
pub fn is_linear(&self) -> bool {
self.kind.is_linear()
}
}
pub trait Symbology {
fn kind(&self) -> SymbologyKind;
fn encode(&self, data: &str) -> Result<Symbol>;
}
pub trait Decoder {
fn kind(&self) -> SymbologyKind;
fn decode(&self, modules: &BitMatrix) -> Result<String>;
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::format;
#[test]
fn matrix_reads_and_writes() {
let mut m = BitMatrix::new(3, 2);
assert!(!m.get(0, 0));
m.set(2, 1, true);
assert!(m.get(2, 1));
assert_eq!(m.row(1), &[false, false, true]);
}
#[test]
fn matrix_ignores_out_of_bounds_access() {
let mut m = BitMatrix::new(2, 2);
m.set(9, 9, true); assert!(!m.get(9, 9));
}
#[test]
fn matrix_reads_out_of_bounds_rows_as_empty() {
let m = BitMatrix::new(3, 2);
assert_eq!(m.row(0).len(), 3);
assert_eq!(m.row(1).len(), 3);
assert!(m.row(2).is_empty());
assert!(m.row(u32::MAX).is_empty());
}
#[test]
fn debug_renders_ascii_art() {
let m = BitMatrix::from_row(vec![true, false, true]);
assert!(format!("{m:?}").contains("#.#"));
}
#[test]
fn linearity_and_character_structure_agree() {
for kind in [SymbologyKind::Code128, SymbologyKind::Qr] {
assert_eq!(
kind.is_linear(),
kind.linear_character().is_some(),
"{kind} disagrees with itself about being linear"
);
}
}
#[test]
fn a_linear_character_is_wider_than_its_element_count() {
for kind in [SymbologyKind::Code128, SymbologyKind::Qr] {
let Some(c) = kind.linear_character() else {
continue;
};
assert!(c.modules >= c.elements, "{kind}: character too narrow");
assert!(
c.stop_modules >= c.stop_elements,
"{kind}: stop pattern too narrow"
);
}
}
#[test]
fn code128_requires_a_ten_module_quiet_zone() {
assert_eq!(SymbologyKind::Code128.required_quiet_zone(), 10);
assert!(SymbologyKind::Code128.is_linear());
}
}