smart_package_tracker/symbology/
mod.rs1#[cfg(feature = "code128")]
13pub mod code128;
14#[cfg(feature = "qr")]
15pub mod qr;
16
17#[cfg(feature = "code128")]
18pub use code128::Code128;
19#[cfg(feature = "qr")]
20pub use qr::{Ecc, Qr, QrVersion};
21
22use alloc::string::String;
23use alloc::vec;
24use alloc::vec::Vec;
25use core::fmt;
26
27use crate::error::Result;
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32#[non_exhaustive]
33pub enum SymbologyKind {
34 Code128,
36 Qr,
40}
41
42impl SymbologyKind {
43 pub fn name(self) -> &'static str {
45 match self {
46 Self::Code128 => "Code 128",
47 Self::Qr => "QR Code",
48 }
49 }
50
51 pub fn is_linear(self) -> bool {
57 match self {
58 Self::Code128 => true,
59 Self::Qr => false,
60 }
61 }
62
63 pub fn required_quiet_zone(self) -> u32 {
69 match self {
70 Self::Code128 => 10,
71 Self::Qr => 4,
72 }
73 }
74}
75
76impl fmt::Display for SymbologyKind {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 f.write_str(self.name())
79 }
80}
81
82#[derive(Clone, PartialEq, Eq)]
87pub struct BitMatrix {
88 width: u32,
89 height: u32,
90 bits: Vec<bool>,
91}
92
93impl BitMatrix {
94 pub fn new(width: u32, height: u32) -> Self {
101 assert!(
102 width > 0 && height > 0,
103 "a symbol must have a positive size"
104 );
105 Self {
106 width,
107 height,
108 bits: vec![false; (width as usize) * (height as usize)],
109 }
110 }
111
112 pub fn from_row(row: Vec<bool>) -> Self {
114 assert!(!row.is_empty(), "a symbol must have a positive size");
115 Self {
116 width: row.len() as u32,
117 height: 1,
118 bits: row,
119 }
120 }
121
122 pub fn from_vec(width: u32, height: u32, bits: Vec<bool>) -> Option<Self> {
127 if width == 0 || height == 0 {
128 return None;
129 }
130 if bits.len() != (width as usize).checked_mul(height as usize)? {
131 return None;
132 }
133 Some(Self {
134 width,
135 height,
136 bits,
137 })
138 }
139
140 pub fn width(&self) -> u32 {
142 self.width
143 }
144
145 pub fn height(&self) -> u32 {
147 self.height
148 }
149
150 pub fn get(&self, x: u32, y: u32) -> bool {
152 if x >= self.width || y >= self.height {
153 return false;
154 }
155 self.bits[(y as usize) * (self.width as usize) + (x as usize)]
156 }
157
158 pub fn set(&mut self, x: u32, y: u32, dark: bool) {
160 if x >= self.width || y >= self.height {
161 return;
162 }
163 let w = self.width as usize;
164 self.bits[(y as usize) * w + (x as usize)] = dark;
165 }
166
167 pub fn row(&self, y: u32) -> &[bool] {
169 let w = self.width as usize;
170 let start = (y as usize) * w;
171 &self.bits[start..start + w]
172 }
173}
174
175impl fmt::Debug for BitMatrix {
176 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177 writeln!(f, "BitMatrix {}x{}", self.width, self.height)?;
178 for y in 0..self.height {
179 for &dark in self.row(y) {
180 f.write_str(if dark { "#" } else { "." })?;
181 }
182 writeln!(f)?;
183 }
184 Ok(())
185 }
186}
187
188#[derive(Clone, Debug, PartialEq, Eq)]
195pub struct Symbol {
196 kind: SymbologyKind,
197 modules: BitMatrix,
198 payload: String,
199}
200
201impl Symbol {
202 pub fn new(kind: SymbologyKind, modules: BitMatrix, payload: String) -> Self {
204 Self {
205 kind,
206 modules,
207 payload,
208 }
209 }
210
211 pub fn kind(&self) -> SymbologyKind {
213 self.kind
214 }
215
216 pub fn modules(&self) -> &BitMatrix {
218 &self.modules
219 }
220
221 pub fn payload(&self) -> &str {
223 &self.payload
224 }
225
226 pub fn is_linear(&self) -> bool {
228 self.kind.is_linear()
229 }
230}
231
232pub trait Symbology {
237 fn kind(&self) -> SymbologyKind;
239
240 fn encode(&self, data: &str) -> Result<Symbol>;
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253 use alloc::format;
254
255 #[test]
256 fn matrix_reads_and_writes() {
257 let mut m = BitMatrix::new(3, 2);
258 assert!(!m.get(0, 0));
259 m.set(2, 1, true);
260 assert!(m.get(2, 1));
261 assert_eq!(m.row(1), &[false, false, true]);
262 }
263
264 #[test]
265 fn matrix_ignores_out_of_bounds_access() {
266 let mut m = BitMatrix::new(2, 2);
267 m.set(9, 9, true); assert!(!m.get(9, 9));
269 }
270
271 #[test]
272 fn debug_renders_ascii_art() {
273 let m = BitMatrix::from_row(vec![true, false, true]);
274 assert!(format!("{m:?}").contains("#.#"));
275 }
276
277 #[test]
278 fn code128_requires_a_ten_module_quiet_zone() {
279 assert_eq!(SymbologyKind::Code128.required_quiet_zone(), 10);
280 assert!(SymbologyKind::Code128.is_linear());
281 }
282}