1use std::{collections::VecDeque, fmt::Display, sync::atomic::AtomicUsize};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub enum BarcodeType {
5 CodeA,
6 CodeB,
7 CodeC,
8}
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum BarcodeValue {
12 RegularCharacter(char),
13 Digit(u8),
14 FNC1,
15 FNC2,
16 FNC3,
17 FNC4,
18 StartA,
19 StartB,
20 StartC,
21 Stop,
22 ShiftA,
23 ShiftB,
24 CodeA,
25 CodeB,
26 CodeC,
27}
28
29impl From<char> for BarcodeValue {
30 fn from(value: char) -> Self {
31 BarcodeValue::RegularCharacter(value)
32 }
33}
34
35impl From<u8> for BarcodeValue {
36 fn from(value: u8) -> Self {
37 BarcodeValue::Digit(value)
38 }
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub struct TableEntry {
43 value: u8,
44 a: BarcodeValue,
45 b: BarcodeValue,
46 c: BarcodeValue,
47 latin: char,
48}
49
50impl TableEntry {
51 pub fn new(
52 value: u8,
53 a: impl Into<BarcodeValue>,
54 b: impl Into<BarcodeValue>,
55 c: impl Into<BarcodeValue>,
56 latin: char,
57 ) -> Self {
58 Self::new_barcode_value(value, a.into(), b.into(), c.into(), latin)
59 }
60
61 const fn new_barcode_value(
62 value: u8,
63 a: BarcodeValue,
64 b: BarcodeValue,
65 c: BarcodeValue,
66 latin: char,
67 ) -> Self {
68 TableEntry {
69 value,
70 a,
71 b,
72 c,
73 latin,
74 }
75 }
76
77 const fn new_barcode_chars(value: u8, a: char, b: char, c: u8, latin: char) -> Self {
78 Self::new_barcode_value(
79 value,
80 BarcodeValue::RegularCharacter(a),
81 BarcodeValue::RegularCharacter(b),
82 BarcodeValue::Digit(c),
83 latin,
84 )
85 }
86
87 pub fn value(&self) -> u8 {
88 self.value
89 }
90
91 pub fn latin(&self) -> char {
92 self.latin
93 }
94}
95
96#[derive(Debug)]
97pub struct Table {
98 entries: [TableEntry; 107],
99}
100
101impl Table {
102 pub const fn new() -> Self {
103 Table {
104 entries: [
105 TableEntry::new_barcode_chars(0, ' ', ' ', 0, ' '),
106 TableEntry::new_barcode_chars(1, '!', '!', 1, '!'),
107 TableEntry::new_barcode_chars(2, '"', '"', 2, '"'),
108 TableEntry::new_barcode_chars(3, '#', '#', 3, '#'),
109 TableEntry::new_barcode_chars(4, '$', '$', 4, '$'),
110 TableEntry::new_barcode_chars(5, '%', '%', 5, '%'),
111 TableEntry::new_barcode_chars(6, '&', '&', 6, '&'),
112 TableEntry::new_barcode_chars(7, '\'', '\'', 7, '\''),
113 TableEntry::new_barcode_chars(8, '(', '(', 8, '('),
114 TableEntry::new_barcode_chars(9, ')', ')', 9, ')'),
115 TableEntry::new_barcode_chars(10, '*', '*', 10, '*'),
116 TableEntry::new_barcode_chars(11, '+', '+', 11, '+'),
117 TableEntry::new_barcode_chars(12, ',', ',', 12, ','),
118 TableEntry::new_barcode_chars(13, '-', '-', 13, '-'),
119 TableEntry::new_barcode_chars(14, '.', '.', 14, '.'),
120 TableEntry::new_barcode_chars(15, '/', '/', 15, '/'),
121 TableEntry::new_barcode_chars(16, '0', '0', 16, '0'),
122 TableEntry::new_barcode_chars(17, '1', '1', 17, '1'),
123 TableEntry::new_barcode_chars(18, '2', '2', 18, '2'),
124 TableEntry::new_barcode_chars(19, '3', '3', 19, '3'),
125 TableEntry::new_barcode_chars(20, '4', '4', 20, '4'),
126 TableEntry::new_barcode_chars(21, '5', '5', 21, '5'),
127 TableEntry::new_barcode_chars(22, '6', '6', 22, '6'),
128 TableEntry::new_barcode_chars(23, '7', '7', 23, '7'),
129 TableEntry::new_barcode_chars(24, '8', '8', 24, '8'),
130 TableEntry::new_barcode_chars(25, '9', '9', 25, '9'),
131 TableEntry::new_barcode_chars(26, ':', ':', 26, ':'),
132 TableEntry::new_barcode_chars(27, ';', ';', 27, ';'),
133 TableEntry::new_barcode_chars(28, '<', '<', 28, '<'),
134 TableEntry::new_barcode_chars(29, '=', '=', 29, '='),
135 TableEntry::new_barcode_chars(30, '>', '>', 30, '>'),
136 TableEntry::new_barcode_chars(31, '?', '?', 31, '?'),
137 TableEntry::new_barcode_chars(32, '@', '@', 32, '@'),
138 TableEntry::new_barcode_chars(33, 'A', 'A', 33, 'A'),
139 TableEntry::new_barcode_chars(34, 'B', 'B', 34, 'B'),
140 TableEntry::new_barcode_chars(35, 'C', 'C', 35, 'C'),
141 TableEntry::new_barcode_chars(36, 'D', 'D', 36, 'D'),
142 TableEntry::new_barcode_chars(37, 'E', 'E', 37, 'E'),
143 TableEntry::new_barcode_chars(38, 'F', 'F', 38, 'F'),
144 TableEntry::new_barcode_chars(39, 'G', 'G', 39, 'G'),
145 TableEntry::new_barcode_chars(40, 'H', 'H', 40, 'H'),
146 TableEntry::new_barcode_chars(41, 'I', 'I', 41, 'I'),
147 TableEntry::new_barcode_chars(42, 'J', 'J', 42, 'J'),
148 TableEntry::new_barcode_chars(43, 'K', 'K', 43, 'K'),
149 TableEntry::new_barcode_chars(44, 'L', 'L', 44, 'L'),
150 TableEntry::new_barcode_chars(45, 'M', 'M', 45, 'M'),
151 TableEntry::new_barcode_chars(46, 'N', 'N', 46, 'N'),
152 TableEntry::new_barcode_chars(47, 'O', 'O', 47, 'O'),
153 TableEntry::new_barcode_chars(48, 'P', 'P', 48, 'P'),
154 TableEntry::new_barcode_chars(49, 'Q', 'Q', 49, 'Q'),
155 TableEntry::new_barcode_chars(50, 'R', 'R', 50, 'R'),
156 TableEntry::new_barcode_chars(51, 'S', 'S', 51, 'S'),
157 TableEntry::new_barcode_chars(52, 'T', 'T', 52, 'T'),
158 TableEntry::new_barcode_chars(53, 'U', 'U', 53, 'U'),
159 TableEntry::new_barcode_chars(54, 'V', 'V', 54, 'V'),
160 TableEntry::new_barcode_chars(55, 'W', 'W', 55, 'W'),
161 TableEntry::new_barcode_chars(56, 'X', 'X', 56, 'X'),
162 TableEntry::new_barcode_chars(57, 'Y', 'Y', 57, 'Y'),
163 TableEntry::new_barcode_chars(58, 'Z', 'Z', 58, 'Z'),
164 TableEntry::new_barcode_chars(59, '[', '[', 59, '['),
165 TableEntry::new_barcode_chars(60, '\\', '\\', 60, '\\'),
166 TableEntry::new_barcode_chars(61, ']', ']', 61, ']'),
167 TableEntry::new_barcode_chars(62, '^', '^', 62, '^'),
168 TableEntry::new_barcode_chars(63, '_', '_', 63, '_'),
169 TableEntry::new_barcode_chars(64, '\x00', '`', 64, '`'),
170 TableEntry::new_barcode_chars(65, '\x01', 'a', 65, 'a'),
171 TableEntry::new_barcode_chars(66, '\x02', 'b', 66, 'b'),
172 TableEntry::new_barcode_chars(67, '\x03', 'c', 67, 'c'),
173 TableEntry::new_barcode_chars(68, '\x04', 'd', 68, 'd'),
174 TableEntry::new_barcode_chars(69, '\x05', 'e', 69, 'e'),
175 TableEntry::new_barcode_chars(70, '\x06', 'f', 70, 'f'),
176 TableEntry::new_barcode_chars(71, '\x07', 'g', 71, 'g'),
177 TableEntry::new_barcode_chars(72, '\x08', 'h', 72, 'h'),
178 TableEntry::new_barcode_chars(73, '\x09', 'i', 73, 'i'),
179 TableEntry::new_barcode_chars(74, '\x0A', 'j', 74, 'j'),
180 TableEntry::new_barcode_chars(75, '\x0B', 'k', 75, 'k'),
181 TableEntry::new_barcode_chars(76, '\x0C', 'l', 76, 'l'),
182 TableEntry::new_barcode_chars(77, '\x0D', 'm', 77, 'm'),
183 TableEntry::new_barcode_chars(78, '\x0E', 'n', 78, 'n'),
184 TableEntry::new_barcode_chars(79, '\x0F', 'o', 79, 'o'),
185 TableEntry::new_barcode_chars(80, '\x10', 'p', 80, 'p'),
186 TableEntry::new_barcode_chars(81, '\x11', 'q', 81, 'q'),
187 TableEntry::new_barcode_chars(82, '\x12', 'r', 82, 'r'),
188 TableEntry::new_barcode_chars(83, '\x13', 's', 83, 's'),
189 TableEntry::new_barcode_chars(84, '\x14', 't', 84, 't'),
190 TableEntry::new_barcode_chars(85, '\x15', 'u', 85, 'u'),
191 TableEntry::new_barcode_chars(86, '\x16', 'v', 86, 'v'),
192 TableEntry::new_barcode_chars(87, '\x17', 'w', 87, 'w'),
193 TableEntry::new_barcode_chars(88, '\x18', 'x', 88, 'x'),
194 TableEntry::new_barcode_chars(89, '\x19', 'y', 89, 'y'),
195 TableEntry::new_barcode_chars(90, '\x1A', 'z', 90, 'z'),
196 TableEntry::new_barcode_chars(91, '\x1B', '{', 91, '{'),
197 TableEntry::new_barcode_chars(92, '\x1C', '|', 92, '|'),
198 TableEntry::new_barcode_chars(93, '\x1D', '}', 93, '}'),
199 TableEntry::new_barcode_chars(94, '\x1E', '~', 94, '~'),
200 TableEntry::new_barcode_chars(95, '\x1F', '\x7F', 95, 'Ã'),
201 TableEntry::new_barcode_value(
202 96,
203 BarcodeValue::FNC3,
204 BarcodeValue::FNC3,
205 BarcodeValue::Digit(96),
206 'Ä',
207 ),
208 TableEntry::new_barcode_value(
209 97,
210 BarcodeValue::FNC2,
211 BarcodeValue::FNC2,
212 BarcodeValue::Digit(97),
213 'Å',
214 ),
215 TableEntry::new_barcode_value(
216 98,
217 BarcodeValue::ShiftB,
218 BarcodeValue::ShiftA,
219 BarcodeValue::Digit(98),
220 'Æ',
221 ),
222 TableEntry::new_barcode_value(
223 99,
224 BarcodeValue::CodeC,
225 BarcodeValue::CodeC,
226 BarcodeValue::Digit(99),
227 'Ç',
228 ),
229 TableEntry::new_barcode_value(
230 100,
231 BarcodeValue::CodeB,
232 BarcodeValue::FNC4,
233 BarcodeValue::CodeB,
234 'È',
235 ),
236 TableEntry::new_barcode_value(
237 101,
238 BarcodeValue::FNC4,
239 BarcodeValue::CodeA,
240 BarcodeValue::CodeA,
241 'É',
242 ),
243 TableEntry::new_barcode_value(
244 102,
245 BarcodeValue::FNC1,
246 BarcodeValue::FNC1,
247 BarcodeValue::FNC1,
248 'Ê',
249 ),
250 TableEntry::new_barcode_value(
251 103,
252 BarcodeValue::StartA,
253 BarcodeValue::StartA,
254 BarcodeValue::StartA,
255 'Ë',
256 ),
257 TableEntry::new_barcode_value(
258 104,
259 BarcodeValue::StartB,
260 BarcodeValue::StartB,
261 BarcodeValue::StartB,
262 'Ì',
263 ),
264 TableEntry::new_barcode_value(
265 105,
266 BarcodeValue::StartC,
267 BarcodeValue::StartC,
268 BarcodeValue::StartC,
269 'Í',
270 ),
271 TableEntry::new_barcode_value(
272 106,
273 BarcodeValue::Stop,
274 BarcodeValue::Stop,
275 BarcodeValue::Stop,
276 'Î',
277 ),
278 ],
279 }
280 }
281
282 pub fn entry_in_set(
283 &self,
284 value: impl Into<BarcodeValue>,
285 set: BarcodeType,
286 ) -> Option<&TableEntry> {
287 let value = value.into();
288 self.entries.iter().find(|entry| match set {
289 BarcodeType::CodeA => entry.a == value,
290 BarcodeType::CodeB => entry.b == value,
291 BarcodeType::CodeC => entry.c == value,
292 })
293 }
294}
295
296#[derive(Debug, PartialEq, Eq)]
297pub struct Barcode<'table> {
298 code: Vec<&'table TableEntry>,
299}
300
301impl PartialOrd for Barcode<'_> {
302 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
303 Some(self.cmp(other))
304 }
305}
306
307impl Ord for Barcode<'_> {
308 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
309 self.code.len().cmp(&other.code.len())
310 }
311}
312
313impl Display for Barcode<'_> {
314 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
315 let mut output = String::new();
316 for entry in &self.code {
317 output.push(entry.latin());
318 }
319 write!(f, "{output}")
320 }
321}
322static TABLE: Table = Table::new();
323
324pub fn make_barcode(text: &str) -> Option<Barcode<'_>> {
325 #[cfg(feature = "parallel")]
326 let res = make_barcode_custom_table_par(&TABLE, text);
327 #[cfg(not(feature = "parallel"))]
328 let res = make_barcode_custom_table(&TABLE, text);
329 res
330}
331
332pub fn make_barcode_custom_table<'table>(
333 table: &'table Table,
334 text: &str,
335) -> Option<Barcode<'table>> {
336 let mut stack = VecDeque::new();
337 stack.push_back(BarcodeBuilder::new(table, text));
338 let mut done = vec![];
339
340 let shortest = AtomicUsize::new(usize::MAX);
341
342 while let Some(partial_code) = stack.pop_front() {
343 let options = partial_code.create_child_options();
344
345 for option in options {
346 if option.remaining.is_empty() {
347 let code = option.build();
348 shortest.fetch_min(code.code.len(), std::sync::atomic::Ordering::SeqCst);
349 done.push(code);
350 } else {
351 if option.code.len() <= shortest.load(std::sync::atomic::Ordering::SeqCst) {
352 stack.push_back(option);
353 }
354 }
355 }
356 }
357
358 done.sort_unstable_by_key(|k| k.code.len());
359
360 done.into_iter().next()
361}
362
363#[cfg(feature = "parallel")]
364pub fn make_barcode_custom_table_par<'table>(
365 table: &'table Table,
366 text: &str,
367) -> Option<Barcode<'table>> {
368 use rayon::iter::ParallelIterator;
369 let walker = rayon::iter::walk_tree(BarcodeBuilder::new(table, text), |t| {
370 t.create_child_options()
371 });
372 let res = walker.min_by_key(|v| {
373 if v.remaining.is_empty() {
374 v.code.len()
375 } else {
376 usize::MAX
377 }
378 });
379 res.map(|r| r.build())
380}
381
382#[derive(Debug, Clone)]
383struct BarcodeBuilder<'table, 's> {
384 table: &'table Table,
385 code: Vec<&'table TableEntry>,
386 remaining: &'s str,
387 set: BarcodeType,
388}
389
390impl<'table, 's> BarcodeBuilder<'table, 's> {
391 fn new(table: &'table Table, code: &'s str) -> Self {
392 Self {
393 table,
394 code: Vec::new(),
395 remaining: code,
396 set: BarcodeType::CodeA,
397 }
398 }
399
400 fn create_child_options(&self) -> Vec<Self> {
401 if self.code.is_empty() {
402 return vec![
403 Self {
404 code: vec![
405 self.table
406 .entry_in_set(BarcodeValue::StartA, BarcodeType::CodeA)
407 .unwrap(),
408 ],
409 ..self.clone()
410 },
411 Self {
412 code: vec![
413 self.table
414 .entry_in_set(BarcodeValue::StartB, BarcodeType::CodeB)
415 .unwrap(),
416 ],
417 set: BarcodeType::CodeB,
418 ..self.clone()
419 },
420 Self {
421 code: vec![
422 self.table
423 .entry_in_set(BarcodeValue::StartC, BarcodeType::CodeC)
424 .unwrap(),
425 ],
426 set: BarcodeType::CodeC,
427 ..self.clone()
428 },
429 ];
430 }
431 let mut options = Vec::new();
432 let old_set = self.set;
433
434 if self.remaining.len() >= 2 {
437 let first_two_characters = &self.remaining[..2];
438 if first_two_characters.chars().all(|c| c.is_ascii_digit()) {
439 if let Ok(v) = first_two_characters.parse::<u8>() {
440 if let Some(s) = self.try_push(|mut s| {
441 if !matches!(old_set, BarcodeType::CodeC) {
442 s.code.push(s.in_set(BarcodeValue::CodeC)?);
443 s.set = BarcodeType::CodeC;
444 }
445 s.code.push(s.in_set(v)?);
446 s.remaining = &s.remaining[2..];
447 Some(s)
448 }) {
449 options.push(s)
450 };
451 }
452 }
453 }
454
455 if let Some(fc) = self.remaining.chars().next() {
457 if let Some(s) = self.try_push(|mut s| {
459 s.code.push(s.in_set(fc)?);
460 s.remaining = &s.remaining[fc.len_utf8()..];
461 Some(s)
462 }) {
463 options.push(s);
464 }
465 for new_set in [BarcodeType::CodeA, BarcodeType::CodeB] {
467 let shift_to = match new_set {
468 BarcodeType::CodeA => BarcodeValue::ShiftA,
469 BarcodeType::CodeB => BarcodeValue::ShiftB,
470 BarcodeType::CodeC => unreachable!(),
471 };
472 if let Some(s) = self.try_push(|mut s| {
473 s.code.push(s.in_set(shift_to)?);
474 s.set = new_set;
475 s.code.push(s.in_set(fc)?);
476 s.set = old_set;
477 s.remaining = &s.remaining[fc.len_utf8()..];
478 Some(s)
479 }) {
480 options.push(s);
481 }
482
483 let force_to = match new_set {
484 BarcodeType::CodeA => BarcodeValue::CodeA,
485 BarcodeType::CodeB => BarcodeValue::CodeB,
486 BarcodeType::CodeC => unreachable!(),
487 };
488
489 if let Some(s) = self.try_push(|mut s| {
490 s.code.push(s.in_set(force_to)?);
491 s.set = new_set;
492 s.code.push(s.in_set(fc)?);
493 s.remaining = &s.remaining[fc.len_utf8()..];
494 Some(s)
495 }) {
496 options.push(s);
497 }
498 }
499 }
500
501 options
502 }
503
504 fn try_push(&self, to_try: impl FnOnce(Self) -> Option<Self>) -> Option<Self> {
505 to_try(self.clone())
506 }
507
508 fn in_set(&self, v: impl Into<BarcodeValue>) -> Option<&'table TableEntry> {
509 self.table.entry_in_set(v, self.set)
510 }
511
512 fn build(mut self) -> Barcode<'table> {
513 let mut total = 0;
514 for (mut idx, character) in self.code.iter().enumerate() {
515 if idx == 0 {
516 idx = 1;
517 }
518 total += character.value() as usize * idx
519 }
520 total %= 103;
521 self.code.push(
522 self.table
523 .entries
524 .iter()
525 .find(|v| v.value() as usize == total)
526 .unwrap(),
527 );
528 self.code.push(
529 self.table
530 .entry_in_set(BarcodeValue::Stop, self.set)
531 .unwrap(),
532 );
533 Barcode { code: self.code }
534 }
535}
536
537