barcode_gen/
lib.rs

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