smart_package_tracker/symbology/
mod.rs1#[cfg(feature = "code128")]
13pub mod code128;
14
15#[cfg(feature = "code128")]
16pub use code128::Code128;
17
18use alloc::string::String;
19use alloc::vec;
20use alloc::vec::Vec;
21use core::fmt;
22
23use crate::error::Result;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28#[non_exhaustive]
29pub enum SymbologyKind {
30 Code128,
32}
33
34impl SymbologyKind {
35 pub fn name(self) -> &'static str {
37 match self {
38 Self::Code128 => "Code 128",
39 }
40 }
41
42 pub fn is_linear(self) -> bool {
48 match self {
49 Self::Code128 => true,
50 }
51 }
52
53 pub fn required_quiet_zone(self) -> u32 {
58 match self {
59 Self::Code128 => 10,
60 }
61 }
62}
63
64impl fmt::Display for SymbologyKind {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 f.write_str(self.name())
67 }
68}
69
70#[derive(Clone, PartialEq, Eq)]
75pub struct BitMatrix {
76 width: u32,
77 height: u32,
78 bits: Vec<bool>,
79}
80
81impl BitMatrix {
82 pub fn new(width: u32, height: u32) -> Self {
89 assert!(
90 width > 0 && height > 0,
91 "a symbol must have a positive size"
92 );
93 Self {
94 width,
95 height,
96 bits: vec![false; (width as usize) * (height as usize)],
97 }
98 }
99
100 pub fn from_row(row: Vec<bool>) -> Self {
102 assert!(!row.is_empty(), "a symbol must have a positive size");
103 Self {
104 width: row.len() as u32,
105 height: 1,
106 bits: row,
107 }
108 }
109
110 pub fn width(&self) -> u32 {
112 self.width
113 }
114
115 pub fn height(&self) -> u32 {
117 self.height
118 }
119
120 pub fn get(&self, x: u32, y: u32) -> bool {
122 if x >= self.width || y >= self.height {
123 return false;
124 }
125 self.bits[(y as usize) * (self.width as usize) + (x as usize)]
126 }
127
128 pub fn set(&mut self, x: u32, y: u32, dark: bool) {
130 if x >= self.width || y >= self.height {
131 return;
132 }
133 let w = self.width as usize;
134 self.bits[(y as usize) * w + (x as usize)] = dark;
135 }
136
137 pub fn row(&self, y: u32) -> &[bool] {
139 let w = self.width as usize;
140 let start = (y as usize) * w;
141 &self.bits[start..start + w]
142 }
143}
144
145impl fmt::Debug for BitMatrix {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 writeln!(f, "BitMatrix {}x{}", self.width, self.height)?;
148 for y in 0..self.height {
149 for &dark in self.row(y) {
150 f.write_str(if dark { "#" } else { "." })?;
151 }
152 writeln!(f)?;
153 }
154 Ok(())
155 }
156}
157
158#[derive(Clone, Debug, PartialEq, Eq)]
165pub struct Symbol {
166 kind: SymbologyKind,
167 modules: BitMatrix,
168 payload: String,
169}
170
171impl Symbol {
172 pub fn new(kind: SymbologyKind, modules: BitMatrix, payload: String) -> Self {
174 Self {
175 kind,
176 modules,
177 payload,
178 }
179 }
180
181 pub fn kind(&self) -> SymbologyKind {
183 self.kind
184 }
185
186 pub fn modules(&self) -> &BitMatrix {
188 &self.modules
189 }
190
191 pub fn payload(&self) -> &str {
193 &self.payload
194 }
195
196 pub fn is_linear(&self) -> bool {
198 self.kind.is_linear()
199 }
200}
201
202pub trait Symbology {
207 fn kind(&self) -> SymbologyKind;
209
210 fn encode(&self, data: &str) -> Result<Symbol>;
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223 use alloc::format;
224
225 #[test]
226 fn matrix_reads_and_writes() {
227 let mut m = BitMatrix::new(3, 2);
228 assert!(!m.get(0, 0));
229 m.set(2, 1, true);
230 assert!(m.get(2, 1));
231 assert_eq!(m.row(1), &[false, false, true]);
232 }
233
234 #[test]
235 fn matrix_ignores_out_of_bounds_access() {
236 let mut m = BitMatrix::new(2, 2);
237 m.set(9, 9, true); assert!(!m.get(9, 9));
239 }
240
241 #[test]
242 fn debug_renders_ascii_art() {
243 let m = BitMatrix::from_row(vec![true, false, true]);
244 assert!(format!("{m:?}").contains("#.#"));
245 }
246
247 #[test]
248 fn code128_requires_a_ten_module_quiet_zone() {
249 assert_eq!(SymbologyKind::Code128.required_quiet_zone(), 10);
250 assert!(SymbologyKind::Code128.is_linear());
251 }
252}