Skip to main content

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