barcode_gen/
lib.rs

1#![warn(missing_docs)]
2//! A Code 128 barcode generator
3
4use std::{
5    collections::{HashMap, VecDeque},
6    fmt::Display,
7    sync::atomic::AtomicUsize,
8};
9
10/// The different barcode types, used to keep track of the type used in the encoder.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum BarcodeType {
13    /// Code 128 variant A, uppper case characters and control characters
14    CodeA,
15    /// Code 128 variant B, upper and lower case characters
16    CodeB,
17    /// Code 128 variant C, two digit numbers
18    CodeC,
19}
20
21/// A value in a barcode, consists of regular characters,
22/// digits for Code C, and control codes.
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum BarcodeValue {
25    /// A regular character, also includes control characters. 
26    RegularCharacter(char),
27    /// A Code C digit, the numbers 0 through 9 are actually 00 through 09. 
28    Digit(u8),
29    /// Used to indicate a GS1-128 barcode
30    FNC1,
31    /// Indicates that the currently scanned string should be prepended to the next scan
32    FNC2,
33    /// Initialize, used for programming scanners
34    FNC3,
35    /// Used for extended ASCII support, currently not available in this crate
36    FNC4,
37    /// The code used to signal we start in variant A
38    StartA,
39    /// The code used to signal we start in variant B
40    StartB,
41    /// The code used to signal we start in variant C
42    StartC,
43    /// Not actually the stop code, just the one used at the end. 
44    Stop,
45    /// Use variant A for the next character
46    ShiftA,
47    /// Use variant B for the next character
48    ShiftB,
49    /// Switch to variant A from here on out
50    CodeA,
51    /// Switch to variant B from here on out
52    CodeB,
53    /// Switch to variant C from here on out
54    CodeC,
55}
56
57impl From<char> for BarcodeValue {
58    fn from(value: char) -> Self {
59        BarcodeValue::RegularCharacter(value)
60    }
61}
62
63impl From<u8> for BarcodeValue {
64    fn from(value: u8) -> Self {
65        BarcodeValue::Digit(value)
66    }
67}
68
69/// An entry in the encoding table.
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub struct TableEntry {
72    value: u8,
73    a: BarcodeValue,
74    b: BarcodeValue,
75    c: BarcodeValue,
76    latin: char,
77}
78
79impl TableEntry {
80    /// create a new table entry
81    pub fn new(
82        value: u8,
83        a: impl Into<BarcodeValue>,
84        b: impl Into<BarcodeValue>,
85        c: impl Into<BarcodeValue>,
86        latin: char,
87    ) -> Self {
88        Self::new_barcode_value(value, a.into(), b.into(), c.into(), latin)
89    }
90
91    const fn new_barcode_value(
92        value: u8,
93        a: BarcodeValue,
94        b: BarcodeValue,
95        c: BarcodeValue,
96        latin: char,
97    ) -> Self {
98        TableEntry {
99            value,
100            a,
101            b,
102            c,
103            latin,
104        }
105    }
106
107    const fn new_barcode_chars(value: u8, a: char, b: char, c: u8, latin: char) -> Self {
108        Self::new_barcode_value(
109            value,
110            BarcodeValue::RegularCharacter(a),
111            BarcodeValue::RegularCharacter(b),
112            BarcodeValue::Digit(c),
113            latin,
114        )
115    }
116
117    /// the value of the entry, used for calculating the check digit
118    pub fn value(&self) -> u8 {
119        self.value
120    }
121
122    /// the most common character used to represent this value
123    pub fn latin(&self) -> char {
124        self.latin
125    }
126
127    /// the bits used to encode the value on a normal barcode
128    pub fn barcode_bits(&self) -> u32 {
129        BARCODE_BITS[self.value() as usize].0
130    }
131
132    /// the runs for the code
133    ///
134    /// A run of 1231 would mean 1001110 in binary
135    pub fn barcode_runs(&self) -> &'static str {
136        BARCODE_BITS[self.value() as usize].1
137    }
138}
139
140const BARCODE_BITS: [(u32, &str); 109] = [
141    (0b11011001100, "212222"),
142    (0b11001101100, "222122"),
143    (0b11001100110, "222221"),
144    (0b10010011000, "121223"),
145    (0b10010001100, "121322"),
146    (0b10001001100, "131222"),
147    (0b10011001000, "122213"),
148    (0b10011000100, "122312"),
149    (0b10001100100, "132212"),
150    (0b11001001000, "221213"),
151    (0b11001000100, "221312"),
152    (0b11000100100, "231212"),
153    (0b10110011100, "112232"),
154    (0b10011011100, "122132"),
155    (0b10011001110, "122231"),
156    (0b10111001100, "113222"),
157    (0b10011101100, "123122"),
158    (0b10011100110, "123221"),
159    (0b11001110010, "223211"),
160    (0b11001011100, "221132"),
161    (0b11001001110, "221231"),
162    (0b11011100100, "213212"),
163    (0b11001110100, "223112"),
164    (0b11101101110, "312131"),
165    (0b11101001100, "311222"),
166    (0b11100101100, "321122"),
167    (0b11100100110, "321221"),
168    (0b11101100100, "312212"),
169    (0b11100110100, "322112"),
170    (0b11100110010, "322211"),
171    (0b11011011000, "212123"),
172    (0b11011000110, "212321"),
173    (0b11000110110, "232121"),
174    (0b10100011000, "111323"),
175    (0b10001011000, "131123"),
176    (0b10001000110, "131321"),
177    (0b10110001000, "112313"),
178    (0b10001101000, "132113"),
179    (0b10001100010, "132311"),
180    (0b11010001000, "211313"),
181    (0b11000101000, "231113"),
182    (0b11000100010, "231311"),
183    (0b10110111000, "112133"),
184    (0b10110001110, "112331"),
185    (0b10001101110, "132131"),
186    (0b10111011000, "113123"),
187    (0b10111000110, "113321"),
188    (0b10001110110, "133121"),
189    (0b11101110110, "313121"),
190    (0b11010001110, "211331"),
191    (0b11000101110, "231131"),
192    (0b11011101000, "213113"),
193    (0b11011100010, "213311"),
194    (0b11011101110, "213131"),
195    (0b11101011000, "311123"),
196    (0b11101000110, "311321"),
197    (0b11100010110, "331121"),
198    (0b11101101000, "312113"),
199    (0b11101100010, "312311"),
200    (0b11100011010, "332111"),
201    (0b11101111010, "314111"),
202    (0b11001000010, "221411"),
203    (0b11110001010, "431111"),
204    (0b10100110000, "111224"),
205    (0b10100001100, "111422"),
206    (0b10010110000, "121124"),
207    (0b10010000110, "121421"),
208    (0b10000101100, "141122"),
209    (0b10000100110, "141221"),
210    (0b10110010000, "112214"),
211    (0b10110000100, "112412"),
212    (0b10011010000, "122114"),
213    (0b10011000010, "122411"),
214    (0b10000110100, "142112"),
215    (0b10000110010, "142211"),
216    (0b11000010010, "241211"),
217    (0b11001010000, "221114"),
218    (0b11110111010, "413111"),
219    (0b11000010100, "241112"),
220    (0b10001111010, "134111"),
221    (0b10100111100, "111242"),
222    (0b10010111100, "121142"),
223    (0b10010011110, "121241"),
224    (0b10111100100, "114212"),
225    (0b10011110100, "124112"),
226    (0b10011110010, "124211"),
227    (0b11110100100, "411212"),
228    (0b11110010100, "421112"),
229    (0b11110010010, "421211"),
230    (0b11011011110, "212141"),
231    (0b11011110110, "214121"),
232    (0b11110110110, "412121"),
233    (0b10101111000, "111143"),
234    (0b10100011110, "111341"),
235    (0b10001011110, "131141"),
236    (0b10111101000, "114113"),
237    (0b10111100010, "114311"),
238    (0b11110101000, "411113"),
239    (0b11110100010, "411311"),
240    (0b10111011110, "113141"),
241    (0b10111101110, "114131"),
242    (0b11101011110, "311141"),
243    (0b11110101110, "411131"),
244    (0b11010000100, "211412"),
245    (0b11010010000, "211214"),
246    (0b11010011100, "211232"),
247    (0b11000111010, "233111"),
248    (0b11010111000, "211133"),
249    (0b1100011101011, "2331112"),
250];
251
252/// an encoding table, used to encode barcodes.
253#[derive(Debug, Clone)]
254pub struct Table {
255    entries: [TableEntry; 107],
256}
257
258impl Table {
259    /// create a new table
260    pub const fn new() -> Self {
261        Table {
262            entries: [
263                TableEntry::new_barcode_chars(0, ' ', ' ', 0, ' '),
264                TableEntry::new_barcode_chars(1, '!', '!', 1, '!'),
265                TableEntry::new_barcode_chars(2, '"', '"', 2, '"'),
266                TableEntry::new_barcode_chars(3, '#', '#', 3, '#'),
267                TableEntry::new_barcode_chars(4, '$', '$', 4, '$'),
268                TableEntry::new_barcode_chars(5, '%', '%', 5, '%'),
269                TableEntry::new_barcode_chars(6, '&', '&', 6, '&'),
270                TableEntry::new_barcode_chars(7, '\'', '\'', 7, '\''),
271                TableEntry::new_barcode_chars(8, '(', '(', 8, '('),
272                TableEntry::new_barcode_chars(9, ')', ')', 9, ')'),
273                TableEntry::new_barcode_chars(10, '*', '*', 10, '*'),
274                TableEntry::new_barcode_chars(11, '+', '+', 11, '+'),
275                TableEntry::new_barcode_chars(12, ',', ',', 12, ','),
276                TableEntry::new_barcode_chars(13, '-', '-', 13, '-'),
277                TableEntry::new_barcode_chars(14, '.', '.', 14, '.'),
278                TableEntry::new_barcode_chars(15, '/', '/', 15, '/'),
279                TableEntry::new_barcode_chars(16, '0', '0', 16, '0'),
280                TableEntry::new_barcode_chars(17, '1', '1', 17, '1'),
281                TableEntry::new_barcode_chars(18, '2', '2', 18, '2'),
282                TableEntry::new_barcode_chars(19, '3', '3', 19, '3'),
283                TableEntry::new_barcode_chars(20, '4', '4', 20, '4'),
284                TableEntry::new_barcode_chars(21, '5', '5', 21, '5'),
285                TableEntry::new_barcode_chars(22, '6', '6', 22, '6'),
286                TableEntry::new_barcode_chars(23, '7', '7', 23, '7'),
287                TableEntry::new_barcode_chars(24, '8', '8', 24, '8'),
288                TableEntry::new_barcode_chars(25, '9', '9', 25, '9'),
289                TableEntry::new_barcode_chars(26, ':', ':', 26, ':'),
290                TableEntry::new_barcode_chars(27, ';', ';', 27, ';'),
291                TableEntry::new_barcode_chars(28, '<', '<', 28, '<'),
292                TableEntry::new_barcode_chars(29, '=', '=', 29, '='),
293                TableEntry::new_barcode_chars(30, '>', '>', 30, '>'),
294                TableEntry::new_barcode_chars(31, '?', '?', 31, '?'),
295                TableEntry::new_barcode_chars(32, '@', '@', 32, '@'),
296                TableEntry::new_barcode_chars(33, 'A', 'A', 33, 'A'),
297                TableEntry::new_barcode_chars(34, 'B', 'B', 34, 'B'),
298                TableEntry::new_barcode_chars(35, 'C', 'C', 35, 'C'),
299                TableEntry::new_barcode_chars(36, 'D', 'D', 36, 'D'),
300                TableEntry::new_barcode_chars(37, 'E', 'E', 37, 'E'),
301                TableEntry::new_barcode_chars(38, 'F', 'F', 38, 'F'),
302                TableEntry::new_barcode_chars(39, 'G', 'G', 39, 'G'),
303                TableEntry::new_barcode_chars(40, 'H', 'H', 40, 'H'),
304                TableEntry::new_barcode_chars(41, 'I', 'I', 41, 'I'),
305                TableEntry::new_barcode_chars(42, 'J', 'J', 42, 'J'),
306                TableEntry::new_barcode_chars(43, 'K', 'K', 43, 'K'),
307                TableEntry::new_barcode_chars(44, 'L', 'L', 44, 'L'),
308                TableEntry::new_barcode_chars(45, 'M', 'M', 45, 'M'),
309                TableEntry::new_barcode_chars(46, 'N', 'N', 46, 'N'),
310                TableEntry::new_barcode_chars(47, 'O', 'O', 47, 'O'),
311                TableEntry::new_barcode_chars(48, 'P', 'P', 48, 'P'),
312                TableEntry::new_barcode_chars(49, 'Q', 'Q', 49, 'Q'),
313                TableEntry::new_barcode_chars(50, 'R', 'R', 50, 'R'),
314                TableEntry::new_barcode_chars(51, 'S', 'S', 51, 'S'),
315                TableEntry::new_barcode_chars(52, 'T', 'T', 52, 'T'),
316                TableEntry::new_barcode_chars(53, 'U', 'U', 53, 'U'),
317                TableEntry::new_barcode_chars(54, 'V', 'V', 54, 'V'),
318                TableEntry::new_barcode_chars(55, 'W', 'W', 55, 'W'),
319                TableEntry::new_barcode_chars(56, 'X', 'X', 56, 'X'),
320                TableEntry::new_barcode_chars(57, 'Y', 'Y', 57, 'Y'),
321                TableEntry::new_barcode_chars(58, 'Z', 'Z', 58, 'Z'),
322                TableEntry::new_barcode_chars(59, '[', '[', 59, '['),
323                TableEntry::new_barcode_chars(60, '\\', '\\', 60, '\\'),
324                TableEntry::new_barcode_chars(61, ']', ']', 61, ']'),
325                TableEntry::new_barcode_chars(62, '^', '^', 62, '^'),
326                TableEntry::new_barcode_chars(63, '_', '_', 63, '_'),
327                TableEntry::new_barcode_chars(64, '\x00', '`', 64, '`'),
328                TableEntry::new_barcode_chars(65, '\x01', 'a', 65, 'a'),
329                TableEntry::new_barcode_chars(66, '\x02', 'b', 66, 'b'),
330                TableEntry::new_barcode_chars(67, '\x03', 'c', 67, 'c'),
331                TableEntry::new_barcode_chars(68, '\x04', 'd', 68, 'd'),
332                TableEntry::new_barcode_chars(69, '\x05', 'e', 69, 'e'),
333                TableEntry::new_barcode_chars(70, '\x06', 'f', 70, 'f'),
334                TableEntry::new_barcode_chars(71, '\x07', 'g', 71, 'g'),
335                TableEntry::new_barcode_chars(72, '\x08', 'h', 72, 'h'),
336                TableEntry::new_barcode_chars(73, '\x09', 'i', 73, 'i'),
337                TableEntry::new_barcode_chars(74, '\x0A', 'j', 74, 'j'),
338                TableEntry::new_barcode_chars(75, '\x0B', 'k', 75, 'k'),
339                TableEntry::new_barcode_chars(76, '\x0C', 'l', 76, 'l'),
340                TableEntry::new_barcode_chars(77, '\x0D', 'm', 77, 'm'),
341                TableEntry::new_barcode_chars(78, '\x0E', 'n', 78, 'n'),
342                TableEntry::new_barcode_chars(79, '\x0F', 'o', 79, 'o'),
343                TableEntry::new_barcode_chars(80, '\x10', 'p', 80, 'p'),
344                TableEntry::new_barcode_chars(81, '\x11', 'q', 81, 'q'),
345                TableEntry::new_barcode_chars(82, '\x12', 'r', 82, 'r'),
346                TableEntry::new_barcode_chars(83, '\x13', 's', 83, 's'),
347                TableEntry::new_barcode_chars(84, '\x14', 't', 84, 't'),
348                TableEntry::new_barcode_chars(85, '\x15', 'u', 85, 'u'),
349                TableEntry::new_barcode_chars(86, '\x16', 'v', 86, 'v'),
350                TableEntry::new_barcode_chars(87, '\x17', 'w', 87, 'w'),
351                TableEntry::new_barcode_chars(88, '\x18', 'x', 88, 'x'),
352                TableEntry::new_barcode_chars(89, '\x19', 'y', 89, 'y'),
353                TableEntry::new_barcode_chars(90, '\x1A', 'z', 90, 'z'),
354                TableEntry::new_barcode_chars(91, '\x1B', '{', 91, '{'),
355                TableEntry::new_barcode_chars(92, '\x1C', '|', 92, '|'),
356                TableEntry::new_barcode_chars(93, '\x1D', '}', 93, '}'),
357                TableEntry::new_barcode_chars(94, '\x1E', '~', 94, '~'),
358                TableEntry::new_barcode_chars(95, '\x1F', '\x7F', 95, 'Ã'),
359                TableEntry::new_barcode_value(
360                    96,
361                    BarcodeValue::FNC3,
362                    BarcodeValue::FNC3,
363                    BarcodeValue::Digit(96),
364                    'Ä',
365                ),
366                TableEntry::new_barcode_value(
367                    97,
368                    BarcodeValue::FNC2,
369                    BarcodeValue::FNC2,
370                    BarcodeValue::Digit(97),
371                    'Å',
372                ),
373                TableEntry::new_barcode_value(
374                    98,
375                    BarcodeValue::ShiftB,
376                    BarcodeValue::ShiftA,
377                    BarcodeValue::Digit(98),
378                    'Æ',
379                ),
380                TableEntry::new_barcode_value(
381                    99,
382                    BarcodeValue::CodeC,
383                    BarcodeValue::CodeC,
384                    BarcodeValue::Digit(99),
385                    'Ç',
386                ),
387                TableEntry::new_barcode_value(
388                    100,
389                    BarcodeValue::CodeB,
390                    BarcodeValue::FNC4,
391                    BarcodeValue::CodeB,
392                    'È',
393                ),
394                TableEntry::new_barcode_value(
395                    101,
396                    BarcodeValue::FNC4,
397                    BarcodeValue::CodeA,
398                    BarcodeValue::CodeA,
399                    'É',
400                ),
401                TableEntry::new_barcode_value(
402                    102,
403                    BarcodeValue::FNC1,
404                    BarcodeValue::FNC1,
405                    BarcodeValue::FNC1,
406                    'Ê',
407                ),
408                TableEntry::new_barcode_value(
409                    103,
410                    BarcodeValue::StartA,
411                    BarcodeValue::StartA,
412                    BarcodeValue::StartA,
413                    'Ë',
414                ),
415                TableEntry::new_barcode_value(
416                    104,
417                    BarcodeValue::StartB,
418                    BarcodeValue::StartB,
419                    BarcodeValue::StartB,
420                    'Ì',
421                ),
422                TableEntry::new_barcode_value(
423                    105,
424                    BarcodeValue::StartC,
425                    BarcodeValue::StartC,
426                    BarcodeValue::StartC,
427                    'Í',
428                ),
429                TableEntry::new_barcode_value(
430                    108,
431                    BarcodeValue::Stop,
432                    BarcodeValue::Stop,
433                    BarcodeValue::Stop,
434                    'Î',
435                ),
436            ],
437        }
438    }
439
440    /// in case you want to make your own (probably incompatible) table
441    ///
442    /// Use the [`Self::modify`] function to change values in the current table,
443    /// like changing what latin character matches a value
444    pub const fn new_from(entries: [TableEntry; 107]) -> Self {
445        Table { entries }
446    }
447
448    /// Modify a table, allowing you to set custom values for all fields of a table entry.
449    ///
450    /// (tip: just make a new table entry and assign it to the parameter)
451    pub fn modify(&self, mut f: impl FnMut(&mut TableEntry)) -> Self {
452        let mut new_entries = self.entries;
453        for entry in &mut new_entries {
454            f(entry);
455        }
456        Table::new_from(new_entries)
457    }
458
459    /// given a requested value and a specific code set, find the value in the table.
460    ///
461    /// It'll return None if the value does not exist in the table, for example when you
462    /// want to use a lower case character but are in Code A.
463    pub fn entry_in_set(
464        &self,
465        value: impl Into<BarcodeValue>,
466        set: BarcodeType,
467    ) -> Option<&TableEntry> {
468        let value = value.into();
469        self.entries.iter().find(|entry| match set {
470            BarcodeType::CodeA => entry.a == value,
471            BarcodeType::CodeB => entry.b == value,
472            BarcodeType::CodeC => entry.c == value,
473        })
474    }
475}
476
477/// A completed barcode.
478#[derive(Debug, PartialEq, Eq)]
479pub struct Barcode<'table> {
480    code: Vec<&'table TableEntry>,
481}
482
483impl PartialOrd for Barcode<'_> {
484    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
485        Some(self.cmp(other))
486    }
487}
488
489impl Ord for Barcode<'_> {
490    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
491        self.code.len().cmp(&other.code.len())
492    }
493}
494
495impl Display for Barcode<'_> {
496    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
497        let mut output = String::new();
498        for entry in &self.code {
499            output.push(entry.latin());
500        }
501        write!(f, "{output}")
502    }
503}
504
505impl<'table> IntoIterator for Barcode<'table> {
506    type Item = &'table TableEntry;
507
508    type IntoIter = std::vec::IntoIter<Self::Item>;
509
510    fn into_iter(self) -> Self::IntoIter {
511        self.code.into_iter()
512    }
513}
514
515/// const value of the most common table.
516pub const TABLE: Table = Table::new();
517
518/// Make a barcode with the fastest available method, currently uses [`make_barcode_custom_table_dp`].
519pub fn make_barcode(text: &str) -> Option<Barcode<'_>> {
520    make_barcode_custom_table_dp(&TABLE, text)
521}
522
523/// Make a barcode using a custom table, you can provide [`TABLE`] to
524/// use the normal table.
525pub fn make_barcode_custom_table<'table>(
526    table: &'table Table,
527    text: &str,
528) -> Option<Barcode<'table>> {
529    let mut stack = VecDeque::new();
530    stack.push_back(BarcodeBuilder::new(table, text));
531    let mut done = vec![];
532
533    let shortest = AtomicUsize::new(usize::MAX);
534
535    while let Some(partial_code) = stack.pop_front() {
536        let options = partial_code.create_child_options();
537
538        for option in options {
539            if option.remaining.is_empty() {
540                let code = option.build();
541                shortest.fetch_min(code.code.len(), std::sync::atomic::Ordering::SeqCst);
542                done.push(code);
543            } else {
544                if option.code.len() <= shortest.load(std::sync::atomic::Ordering::SeqCst) {
545                    stack.push_back(option);
546                }
547            }
548        }
549    }
550
551    done.sort_unstable_by_key(|k| k.code.len());
552
553    done.into_iter().next()
554}
555
556/// like [`make_barcode_custom_table`], but uses [`rayon`] to be up to 10 times faster.
557#[cfg(feature = "parallel")]
558pub fn make_barcode_custom_table_par<'table>(
559    table: &'table Table,
560    text: &str,
561) -> Option<Barcode<'table>> {
562    use rayon::iter::ParallelIterator;
563    let walker = rayon::iter::walk_tree(BarcodeBuilder::new(table, text), |t| {
564        t.create_child_options()
565    });
566    let res = walker.min_by_key(|v| {
567        if v.remaining.is_empty() {
568            v.code.len()
569        } else {
570            usize::MAX
571        }
572    });
573    res.map(|r| r.build())
574}
575
576/// Find a barcode using dynamic programming. It's very fast.
577pub fn make_barcode_custom_table_dp<'table>(
578    table: &'table Table,
579    mut text: &str,
580) -> Option<Barcode<'table>> {
581    let mut map = HashMap::<(&str, BarcodeType), BarcodeBuilder>::new();
582    for c in BarcodeBuilder::new(table, text).create_child_options() {
583        map.insert((c.remaining, c.set), c);
584    }
585
586    while !text.is_empty() {
587        for ty in [BarcodeType::CodeA, BarcodeType::CodeB, BarcodeType::CodeC] {
588            if let Some(builder) = map.get(&(text, ty)) {
589                let options = builder.create_child_options();
590                for option in options {
591                    if let Some(c) = map.get(&(option.remaining, option.set)) {
592                        if c.cost() > option.cost() {
593                            map.insert((option.remaining, option.set), option);
594                        }
595                    } else {
596                        map.insert((option.remaining, option.set), option);
597                    }
598                }
599            }
600        }
601        text = &text[1..];
602    }
603
604    map.into_iter()
605        .filter_map(|((k, _), v)| if k.is_empty() { Some(v.build()) } else { None })
606        .min_by_key(|v| v.code.len())
607}
608
609#[derive(Debug, Clone)]
610struct BarcodeBuilder<'table, 's> {
611    table: &'table Table,
612    code: Vec<&'table TableEntry>,
613    remaining: &'s str,
614    set: BarcodeType,
615}
616
617impl<'table, 's> BarcodeBuilder<'table, 's> {
618    fn new(table: &'table Table, code: &'s str) -> Self {
619        Self {
620            table,
621            code: Vec::new(),
622            remaining: code,
623            set: BarcodeType::CodeA,
624        }
625    }
626
627    fn cost(&self) -> usize {
628        self.code.len()
629    }
630
631    fn create_child_options(&self) -> Vec<Self> {
632        if self.code.is_empty() {
633            return vec![
634                Self {
635                    code: vec![
636                        self.table
637                            .entry_in_set(BarcodeValue::StartA, BarcodeType::CodeA)
638                            .unwrap(),
639                    ],
640                    ..self.clone()
641                },
642                Self {
643                    code: vec![
644                        self.table
645                            .entry_in_set(BarcodeValue::StartB, BarcodeType::CodeB)
646                            .unwrap(),
647                    ],
648                    set: BarcodeType::CodeB,
649                    ..self.clone()
650                },
651                Self {
652                    code: vec![
653                        self.table
654                            .entry_in_set(BarcodeValue::StartC, BarcodeType::CodeC)
655                            .unwrap(),
656                    ],
657                    set: BarcodeType::CodeC,
658                    ..self.clone()
659                },
660            ];
661        }
662        let mut options = Vec::new();
663        let old_set = self.set;
664
665        // try to see if set C works, always two numbers
666
667        if self.remaining.len() >= 2 {
668            let first_two_characters = &self.remaining[..2];
669            if first_two_characters.chars().all(|c| c.is_ascii_digit()) {
670                if let Ok(v) = first_two_characters.parse::<u8>() {
671                    if let Some(s) = self.try_push(|mut s| {
672                        if !matches!(old_set, BarcodeType::CodeC) {
673                            s.code.push(s.in_set(BarcodeValue::CodeC)?);
674                            s.set = BarcodeType::CodeC;
675                        }
676                        s.code.push(s.in_set(v)?);
677                        s.remaining = &s.remaining[2..];
678                        Some(s)
679                    }) {
680                        options.push(s)
681                    };
682                }
683            }
684        }
685
686        // single character, so set A or B
687        if let Some(fc) = self.remaining.chars().next() {
688            // does it exist in the current set?
689            if let Some(s) = self.try_push(|mut s| {
690                s.code.push(s.in_set(fc)?);
691                s.remaining = &s.remaining[fc.len_utf8()..];
692                Some(s)
693            }) {
694                options.push(s);
695            }
696            // shift A or B?
697            for new_set in [BarcodeType::CodeA, BarcodeType::CodeB] {
698                let shift_to = match new_set {
699                    BarcodeType::CodeA => BarcodeValue::ShiftA,
700                    BarcodeType::CodeB => BarcodeValue::ShiftB,
701                    BarcodeType::CodeC => unreachable!(),
702                };
703                if let Some(s) = self.try_push(|mut s| {
704                    s.code.push(s.in_set(shift_to)?);
705                    s.set = new_set;
706                    s.code.push(s.in_set(fc)?);
707                    s.set = old_set;
708                    s.remaining = &s.remaining[fc.len_utf8()..];
709                    Some(s)
710                }) {
711                    options.push(s);
712                }
713
714                let force_to = match new_set {
715                    BarcodeType::CodeA => BarcodeValue::CodeA,
716                    BarcodeType::CodeB => BarcodeValue::CodeB,
717                    BarcodeType::CodeC => unreachable!(),
718                };
719
720                if let Some(s) = self.try_push(|mut s| {
721                    s.code.push(s.in_set(force_to)?);
722                    s.set = new_set;
723                    s.code.push(s.in_set(fc)?);
724                    s.remaining = &s.remaining[fc.len_utf8()..];
725                    Some(s)
726                }) {
727                    options.push(s);
728                }
729            }
730        }
731
732        options
733    }
734
735    fn try_push(&self, to_try: impl FnOnce(Self) -> Option<Self>) -> Option<Self> {
736        to_try(self.clone())
737    }
738
739    fn in_set(&self, v: impl Into<BarcodeValue>) -> Option<&'table TableEntry> {
740        self.table.entry_in_set(v, self.set)
741    }
742
743    fn build(mut self) -> Barcode<'table> {
744        let mut total = 0;
745        for (mut idx, character) in self.code.iter().enumerate() {
746            if idx == 0 {
747                idx = 1;
748            }
749            total += character.value() as usize * idx
750        }
751        total %= 103;
752        self.code.push(
753            self.table
754                .entries
755                .iter()
756                .find(|v| v.value() as usize == total)
757                .unwrap(),
758        );
759        self.code.push(
760            self.table
761                .entry_in_set(BarcodeValue::Stop, self.set)
762                .unwrap(),
763        );
764        Barcode { code: self.code }
765    }
766}
767
768#[cfg(test)]
769mod tests {
770    use rand::Rng;
771
772    #[test]
773    fn test_equality() {
774        let mut rng = rand::rng();
775        let len = rng.random_range(1..20);
776        let random_string = rng
777            .sample_iter(rand::distr::Alphanumeric)
778            .take(len)
779            .map(|c| c as char)
780            .collect::<String>();
781
782        let code_one = super::make_barcode_custom_table(&super::TABLE, &random_string);
783        #[cfg(feature = "parallel")]
784        let code_two = super::make_barcode_custom_table_par(&super::TABLE, &random_string);
785        let code_three = super::make_barcode_custom_table_dp(&super::TABLE, &random_string);
786        #[cfg(feature = "parallel")]
787        assert_eq!(code_one, code_two);
788        assert_eq!(code_one, code_three);
789        #[cfg(feature = "parallel")]
790        assert_eq!(code_two, code_three);
791    }
792}