barcode_gen/
lib.rs

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