#![warn(missing_docs)]
use std::{collections::HashMap, fmt::Display};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BarcodeType {
CodeA,
CodeB,
CodeC,
}
impl BarcodeType {
fn other_set(&self) -> Option<Self> {
match self {
BarcodeType::CodeA => Some(BarcodeType::CodeB),
BarcodeType::CodeB => Some(BarcodeType::CodeA),
BarcodeType::CodeC => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BarcodeValue {
RegularCharacter(char),
Digit(u8),
FNC1,
FNC2,
FNC3,
FNC4,
StartA,
StartB,
StartC,
Stop,
ShiftA,
ShiftB,
CodeA,
CodeB,
CodeC,
}
impl From<char> for BarcodeValue {
fn from(value: char) -> Self {
BarcodeValue::RegularCharacter(value)
}
}
impl From<u8> for BarcodeValue {
fn from(value: u8) -> Self {
BarcodeValue::Digit(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TableEntry {
value: u8,
pub a: BarcodeValue,
pub b: BarcodeValue,
pub c: BarcodeValue,
latin: char,
}
impl TableEntry {
pub fn new(
value: u8,
a: impl Into<BarcodeValue>,
b: impl Into<BarcodeValue>,
c: impl Into<BarcodeValue>,
latin: char,
) -> Self {
Self::new_barcode_value(value, a.into(), b.into(), c.into(), latin)
}
const fn new_barcode_value(
value: u8,
a: BarcodeValue,
b: BarcodeValue,
c: BarcodeValue,
latin: char,
) -> Self {
TableEntry {
value,
a,
b,
c,
latin,
}
}
const fn new_barcode_chars(value: u8, a: char, b: char, c: u8, latin: char) -> Self {
Self::new_barcode_value(
value,
BarcodeValue::RegularCharacter(a),
BarcodeValue::RegularCharacter(b),
BarcodeValue::Digit(c),
latin,
)
}
pub fn value(&self) -> u8 {
self.value
}
pub fn latin(&self) -> char {
self.latin
}
pub fn barcode_bits(&self) -> u32 {
BARCODE_BITS[self.value() as usize].0
}
pub fn barcode_runs(&self) -> &'static str {
BARCODE_BITS[self.value() as usize].1
}
}
const BARCODE_BITS: [(u32, &str); 109] = [
(0b11011001100, "212222"),
(0b11001101100, "222122"),
(0b11001100110, "222221"),
(0b10010011000, "121223"),
(0b10010001100, "121322"),
(0b10001001100, "131222"),
(0b10011001000, "122213"),
(0b10011000100, "122312"),
(0b10001100100, "132212"),
(0b11001001000, "221213"),
(0b11001000100, "221312"),
(0b11000100100, "231212"),
(0b10110011100, "112232"),
(0b10011011100, "122132"),
(0b10011001110, "122231"),
(0b10111001100, "113222"),
(0b10011101100, "123122"),
(0b10011100110, "123221"),
(0b11001110010, "223211"),
(0b11001011100, "221132"),
(0b11001001110, "221231"),
(0b11011100100, "213212"),
(0b11001110100, "223112"),
(0b11101101110, "312131"),
(0b11101001100, "311222"),
(0b11100101100, "321122"),
(0b11100100110, "321221"),
(0b11101100100, "312212"),
(0b11100110100, "322112"),
(0b11100110010, "322211"),
(0b11011011000, "212123"),
(0b11011000110, "212321"),
(0b11000110110, "232121"),
(0b10100011000, "111323"),
(0b10001011000, "131123"),
(0b10001000110, "131321"),
(0b10110001000, "112313"),
(0b10001101000, "132113"),
(0b10001100010, "132311"),
(0b11010001000, "211313"),
(0b11000101000, "231113"),
(0b11000100010, "231311"),
(0b10110111000, "112133"),
(0b10110001110, "112331"),
(0b10001101110, "132131"),
(0b10111011000, "113123"),
(0b10111000110, "113321"),
(0b10001110110, "133121"),
(0b11101110110, "313121"),
(0b11010001110, "211331"),
(0b11000101110, "231131"),
(0b11011101000, "213113"),
(0b11011100010, "213311"),
(0b11011101110, "213131"),
(0b11101011000, "311123"),
(0b11101000110, "311321"),
(0b11100010110, "331121"),
(0b11101101000, "312113"),
(0b11101100010, "312311"),
(0b11100011010, "332111"),
(0b11101111010, "314111"),
(0b11001000010, "221411"),
(0b11110001010, "431111"),
(0b10100110000, "111224"),
(0b10100001100, "111422"),
(0b10010110000, "121124"),
(0b10010000110, "121421"),
(0b10000101100, "141122"),
(0b10000100110, "141221"),
(0b10110010000, "112214"),
(0b10110000100, "112412"),
(0b10011010000, "122114"),
(0b10011000010, "122411"),
(0b10000110100, "142112"),
(0b10000110010, "142211"),
(0b11000010010, "241211"),
(0b11001010000, "221114"),
(0b11110111010, "413111"),
(0b11000010100, "241112"),
(0b10001111010, "134111"),
(0b10100111100, "111242"),
(0b10010111100, "121142"),
(0b10010011110, "121241"),
(0b10111100100, "114212"),
(0b10011110100, "124112"),
(0b10011110010, "124211"),
(0b11110100100, "411212"),
(0b11110010100, "421112"),
(0b11110010010, "421211"),
(0b11011011110, "212141"),
(0b11011110110, "214121"),
(0b11110110110, "412121"),
(0b10101111000, "111143"),
(0b10100011110, "111341"),
(0b10001011110, "131141"),
(0b10111101000, "114113"),
(0b10111100010, "114311"),
(0b11110101000, "411113"),
(0b11110100010, "411311"),
(0b10111011110, "113141"),
(0b10111101110, "114131"),
(0b11101011110, "311141"),
(0b11110101110, "411131"),
(0b11010000100, "211412"),
(0b11010010000, "211214"),
(0b11010011100, "211232"),
(0b11000111010, "233111"),
(0b11010111000, "211133"),
(0b1100011101011, "2331112"),
];
#[derive(Debug, Clone)]
pub struct Table {
entries: [TableEntry; 107],
}
impl Table {
pub const fn new() -> Self {
Table {
entries: [
TableEntry::new_barcode_chars(0, ' ', ' ', 0, ' '),
TableEntry::new_barcode_chars(1, '!', '!', 1, '!'),
TableEntry::new_barcode_chars(2, '"', '"', 2, '"'),
TableEntry::new_barcode_chars(3, '#', '#', 3, '#'),
TableEntry::new_barcode_chars(4, '$', '$', 4, '$'),
TableEntry::new_barcode_chars(5, '%', '%', 5, '%'),
TableEntry::new_barcode_chars(6, '&', '&', 6, '&'),
TableEntry::new_barcode_chars(7, '\'', '\'', 7, '\''),
TableEntry::new_barcode_chars(8, '(', '(', 8, '('),
TableEntry::new_barcode_chars(9, ')', ')', 9, ')'),
TableEntry::new_barcode_chars(10, '*', '*', 10, '*'),
TableEntry::new_barcode_chars(11, '+', '+', 11, '+'),
TableEntry::new_barcode_chars(12, ',', ',', 12, ','),
TableEntry::new_barcode_chars(13, '-', '-', 13, '-'),
TableEntry::new_barcode_chars(14, '.', '.', 14, '.'),
TableEntry::new_barcode_chars(15, '/', '/', 15, '/'),
TableEntry::new_barcode_chars(16, '0', '0', 16, '0'),
TableEntry::new_barcode_chars(17, '1', '1', 17, '1'),
TableEntry::new_barcode_chars(18, '2', '2', 18, '2'),
TableEntry::new_barcode_chars(19, '3', '3', 19, '3'),
TableEntry::new_barcode_chars(20, '4', '4', 20, '4'),
TableEntry::new_barcode_chars(21, '5', '5', 21, '5'),
TableEntry::new_barcode_chars(22, '6', '6', 22, '6'),
TableEntry::new_barcode_chars(23, '7', '7', 23, '7'),
TableEntry::new_barcode_chars(24, '8', '8', 24, '8'),
TableEntry::new_barcode_chars(25, '9', '9', 25, '9'),
TableEntry::new_barcode_chars(26, ':', ':', 26, ':'),
TableEntry::new_barcode_chars(27, ';', ';', 27, ';'),
TableEntry::new_barcode_chars(28, '<', '<', 28, '<'),
TableEntry::new_barcode_chars(29, '=', '=', 29, '='),
TableEntry::new_barcode_chars(30, '>', '>', 30, '>'),
TableEntry::new_barcode_chars(31, '?', '?', 31, '?'),
TableEntry::new_barcode_chars(32, '@', '@', 32, '@'),
TableEntry::new_barcode_chars(33, 'A', 'A', 33, 'A'),
TableEntry::new_barcode_chars(34, 'B', 'B', 34, 'B'),
TableEntry::new_barcode_chars(35, 'C', 'C', 35, 'C'),
TableEntry::new_barcode_chars(36, 'D', 'D', 36, 'D'),
TableEntry::new_barcode_chars(37, 'E', 'E', 37, 'E'),
TableEntry::new_barcode_chars(38, 'F', 'F', 38, 'F'),
TableEntry::new_barcode_chars(39, 'G', 'G', 39, 'G'),
TableEntry::new_barcode_chars(40, 'H', 'H', 40, 'H'),
TableEntry::new_barcode_chars(41, 'I', 'I', 41, 'I'),
TableEntry::new_barcode_chars(42, 'J', 'J', 42, 'J'),
TableEntry::new_barcode_chars(43, 'K', 'K', 43, 'K'),
TableEntry::new_barcode_chars(44, 'L', 'L', 44, 'L'),
TableEntry::new_barcode_chars(45, 'M', 'M', 45, 'M'),
TableEntry::new_barcode_chars(46, 'N', 'N', 46, 'N'),
TableEntry::new_barcode_chars(47, 'O', 'O', 47, 'O'),
TableEntry::new_barcode_chars(48, 'P', 'P', 48, 'P'),
TableEntry::new_barcode_chars(49, 'Q', 'Q', 49, 'Q'),
TableEntry::new_barcode_chars(50, 'R', 'R', 50, 'R'),
TableEntry::new_barcode_chars(51, 'S', 'S', 51, 'S'),
TableEntry::new_barcode_chars(52, 'T', 'T', 52, 'T'),
TableEntry::new_barcode_chars(53, 'U', 'U', 53, 'U'),
TableEntry::new_barcode_chars(54, 'V', 'V', 54, 'V'),
TableEntry::new_barcode_chars(55, 'W', 'W', 55, 'W'),
TableEntry::new_barcode_chars(56, 'X', 'X', 56, 'X'),
TableEntry::new_barcode_chars(57, 'Y', 'Y', 57, 'Y'),
TableEntry::new_barcode_chars(58, 'Z', 'Z', 58, 'Z'),
TableEntry::new_barcode_chars(59, '[', '[', 59, '['),
TableEntry::new_barcode_chars(60, '\\', '\\', 60, '\\'),
TableEntry::new_barcode_chars(61, ']', ']', 61, ']'),
TableEntry::new_barcode_chars(62, '^', '^', 62, '^'),
TableEntry::new_barcode_chars(63, '_', '_', 63, '_'),
TableEntry::new_barcode_chars(64, '\x00', '`', 64, '`'),
TableEntry::new_barcode_chars(65, '\x01', 'a', 65, 'a'),
TableEntry::new_barcode_chars(66, '\x02', 'b', 66, 'b'),
TableEntry::new_barcode_chars(67, '\x03', 'c', 67, 'c'),
TableEntry::new_barcode_chars(68, '\x04', 'd', 68, 'd'),
TableEntry::new_barcode_chars(69, '\x05', 'e', 69, 'e'),
TableEntry::new_barcode_chars(70, '\x06', 'f', 70, 'f'),
TableEntry::new_barcode_chars(71, '\x07', 'g', 71, 'g'),
TableEntry::new_barcode_chars(72, '\x08', 'h', 72, 'h'),
TableEntry::new_barcode_chars(73, '\x09', 'i', 73, 'i'),
TableEntry::new_barcode_chars(74, '\x0A', 'j', 74, 'j'),
TableEntry::new_barcode_chars(75, '\x0B', 'k', 75, 'k'),
TableEntry::new_barcode_chars(76, '\x0C', 'l', 76, 'l'),
TableEntry::new_barcode_chars(77, '\x0D', 'm', 77, 'm'),
TableEntry::new_barcode_chars(78, '\x0E', 'n', 78, 'n'),
TableEntry::new_barcode_chars(79, '\x0F', 'o', 79, 'o'),
TableEntry::new_barcode_chars(80, '\x10', 'p', 80, 'p'),
TableEntry::new_barcode_chars(81, '\x11', 'q', 81, 'q'),
TableEntry::new_barcode_chars(82, '\x12', 'r', 82, 'r'),
TableEntry::new_barcode_chars(83, '\x13', 's', 83, 's'),
TableEntry::new_barcode_chars(84, '\x14', 't', 84, 't'),
TableEntry::new_barcode_chars(85, '\x15', 'u', 85, 'u'),
TableEntry::new_barcode_chars(86, '\x16', 'v', 86, 'v'),
TableEntry::new_barcode_chars(87, '\x17', 'w', 87, 'w'),
TableEntry::new_barcode_chars(88, '\x18', 'x', 88, 'x'),
TableEntry::new_barcode_chars(89, '\x19', 'y', 89, 'y'),
TableEntry::new_barcode_chars(90, '\x1A', 'z', 90, 'z'),
TableEntry::new_barcode_chars(91, '\x1B', '{', 91, '{'),
TableEntry::new_barcode_chars(92, '\x1C', '|', 92, '|'),
TableEntry::new_barcode_chars(93, '\x1D', '}', 93, '}'),
TableEntry::new_barcode_chars(94, '\x1E', '~', 94, '~'),
TableEntry::new_barcode_chars(95, '\x1F', '\x7F', 95, 'Ã'),
TableEntry::new_barcode_value(
96,
BarcodeValue::FNC3,
BarcodeValue::FNC3,
BarcodeValue::Digit(96),
'Ä',
),
TableEntry::new_barcode_value(
97,
BarcodeValue::FNC2,
BarcodeValue::FNC2,
BarcodeValue::Digit(97),
'Å',
),
TableEntry::new_barcode_value(
98,
BarcodeValue::ShiftB,
BarcodeValue::ShiftA,
BarcodeValue::Digit(98),
'Æ',
),
TableEntry::new_barcode_value(
99,
BarcodeValue::CodeC,
BarcodeValue::CodeC,
BarcodeValue::Digit(99),
'Ç',
),
TableEntry::new_barcode_value(
100,
BarcodeValue::CodeB,
BarcodeValue::FNC4,
BarcodeValue::CodeB,
'È',
),
TableEntry::new_barcode_value(
101,
BarcodeValue::FNC4,
BarcodeValue::CodeA,
BarcodeValue::CodeA,
'É',
),
TableEntry::new_barcode_value(
102,
BarcodeValue::FNC1,
BarcodeValue::FNC1,
BarcodeValue::FNC1,
'Ê',
),
TableEntry::new_barcode_value(
103,
BarcodeValue::StartA,
BarcodeValue::StartA,
BarcodeValue::StartA,
'Ë',
),
TableEntry::new_barcode_value(
104,
BarcodeValue::StartB,
BarcodeValue::StartB,
BarcodeValue::StartB,
'Ì',
),
TableEntry::new_barcode_value(
105,
BarcodeValue::StartC,
BarcodeValue::StartC,
BarcodeValue::StartC,
'Í',
),
TableEntry::new_barcode_value(
108,
BarcodeValue::Stop,
BarcodeValue::Stop,
BarcodeValue::Stop,
'Î',
),
],
}
}
pub const fn new_from(entries: [TableEntry; 107]) -> Self {
Table { entries }
}
pub fn modify(&self, mut f: impl FnMut(&mut TableEntry)) -> Self {
let mut new_entries = self.entries;
for entry in &mut new_entries {
f(entry);
}
Table::new_from(new_entries)
}
pub fn entry_in_set(
&self,
value: impl Into<BarcodeValue>,
set: BarcodeType,
) -> Option<&TableEntry> {
let value = value.into();
self.entries.iter().find(|entry| match set {
BarcodeType::CodeA => entry.a == value,
BarcodeType::CodeB => entry.b == value,
BarcodeType::CodeC => entry.c == value,
})
}
}
impl Default for Table {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct Barcode<'table> {
code: Vec<&'table TableEntry>,
}
impl PartialOrd for Barcode<'_> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Barcode<'_> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.code.len().cmp(&other.code.len())
}
}
impl Display for Barcode<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut output = String::new();
for entry in &self.code {
output.push(entry.latin());
}
write!(f, "{output}")
}
}
impl<'table> IntoIterator for Barcode<'table> {
type Item = &'table TableEntry;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.code.into_iter()
}
}
pub const TABLE: Table = Table::new();
pub fn make_barcode(text: &str) -> Option<Barcode<'_>> {
make_barcode_prefer_set(text, BarcodeType::CodeB)
}
pub fn make_barcode_prefer_set(text: &str, prefer: BarcodeType) -> Option<Barcode<'_>> {
make_barcode_custom_table(&TABLE, text, prefer)
}
pub fn make_barcode_custom_table<'table>(
table: &'table Table,
mut text: &str,
prefer: BarcodeType,
) -> Option<Barcode<'table>> {
let mut map = HashMap::new();
for c in BarcodeBuilder::new(table, text) {
map.insert(c.key(), c);
}
while !text.is_empty() {
let next_char = text.chars().next().unwrap().len_utf8();
for ty in [BarcodeType::CodeA, BarcodeType::CodeB, BarcodeType::CodeC] {
for fnc_state in [false, true] {
if let Some(builder) = map.get(&BarcodeKey::new(text, ty).with_fnc(fnc_state)) {
let options = builder.create_child_options();
for current in options {
let key = current.key();
if let Some(previous) = map.get(&key) {
let prev_cost = previous.cost();
let curr_cost = current.cost();
if prev_cost > curr_cost || key.set == prefer && prev_cost == curr_cost
{
map.insert(key, current);
}
} else {
map.insert(key, current);
}
}
}
}
}
text = &text[next_char..];
}
map.into_iter()
.filter_map(|(k, v)| if k.is_empty() { Some(v.build()) } else { None })
.min_by_key(|v| v.code.len())
}
#[derive(Debug, Clone)]
struct BarcodeBuilder<'table, 's> {
table: &'table Table,
code: Vec<&'table TableEntry>,
key: BarcodeKey<'s>,
}
impl<'table, 's> BarcodeBuilder<'table, 's> {
fn new(table: &'table Table, code: &'s str) -> [BarcodeBuilder<'table, 's>; 3] {
[
Self {
code: vec![
table
.entry_in_set(BarcodeValue::StartA, BarcodeType::CodeA)
.unwrap(),
],
table,
key: BarcodeKey::new(code, BarcodeType::CodeA),
},
Self {
code: vec![
table
.entry_in_set(BarcodeValue::StartB, BarcodeType::CodeB)
.unwrap(),
],
table,
key: BarcodeKey::new(code, BarcodeType::CodeB),
},
Self {
code: vec![
table
.entry_in_set(BarcodeValue::StartC, BarcodeType::CodeC)
.unwrap(),
],
table,
key: BarcodeKey::new(code, BarcodeType::CodeC),
},
]
}
fn cost(&self) -> usize {
self.code.len()
}
fn create_child_options(&self) -> Vec<Self> {
let mut options = Vec::new();
let old_set = self.key.set;
if self.key.remaining.len() >= 2 {
let first_two_characters = self.key.remaining.chars().take(2).collect::<String>();
if first_two_characters.chars().all(|c| c.is_ascii_digit()) {
if let Ok(v) = first_two_characters.parse::<u8>() {
if let Some(s) = self.try_push(|mut s| {
if !matches!(old_set, BarcodeType::CodeC) {
s.code.push(s.in_set(BarcodeValue::CodeC)?);
s.key.set = BarcodeType::CodeC;
}
s.code.push(s.in_set(v)?);
s.key.remaining = &s.key.remaining[first_two_characters.len()..];
Some(s)
}) {
options.push(s)
};
}
}
}
if let Some(fc) = self.key.remaining.chars().next() {
if let Some(s) = self.try_push(|mut s| {
s.code.push(s.in_set(fc)?);
s.key.remaining = &s.key.remaining[fc.len_utf8()..];
Some(s)
}) {
options.push(s);
}
if let Some(s) = self.try_push(|mut s| {
s.code.push(s.in_set(BarcodeValue::FNC4)?);
s.key.fnc4 = !s.key.fnc4;
s.code.push(s.in_set(fc)?);
s.key.fnc4 = !s.key.fnc4;
s.key.remaining = &s.key.remaining[fc.len_utf8()..];
Some(s)
}) {
options.push(s);
}
if let Some(s) = self.try_push(|mut s| {
s.code.push(s.in_set(BarcodeValue::FNC4)?);
s.code.push(s.in_set(BarcodeValue::FNC4)?);
s.key.fnc4 = !s.key.fnc4;
s.code.push(s.in_set(fc)?);
s.key.remaining = &s.key.remaining[fc.len_utf8()..];
Some(s)
}) {
options.push(s);
}
if let Some(new_set) = old_set.other_set() {
let shift_to = match new_set {
BarcodeType::CodeA => BarcodeValue::ShiftA,
BarcodeType::CodeB => BarcodeValue::ShiftB,
BarcodeType::CodeC => unreachable!(),
};
if let Some(s) = self.try_push(|mut s| {
s.code.push(s.in_set(shift_to)?);
s.key.set = new_set;
s.code.push(s.in_set(fc)?);
s.key.set = old_set;
s.key.remaining = &s.key.remaining[fc.len_utf8()..];
Some(s)
}) {
options.push(s);
}
let force_to = match new_set {
BarcodeType::CodeA => BarcodeValue::CodeA,
BarcodeType::CodeB => BarcodeValue::CodeB,
BarcodeType::CodeC => unreachable!(),
};
if let Some(s) = self.try_push(|mut s| {
s.code.push(s.in_set(force_to)?);
s.key.set = new_set;
s.code.push(s.in_set(fc)?);
s.key.remaining = &s.key.remaining[fc.len_utf8()..];
Some(s)
}) {
options.push(s);
}
}
}
options
}
fn try_push(&self, to_try: impl FnOnce(Self) -> Option<Self>) -> Option<Self> {
to_try(self.clone())
}
fn in_set(&self, v: impl Into<BarcodeValue>) -> Option<&'table TableEntry> {
let v = v.into();
if self.key.fnc4 {
if let BarcodeValue::RegularCharacter(c) = v {
let c = (c as u32).checked_sub(128)?;
let c = char::from_u32(c)?;
return self.table.entry_in_set(c, self.key.set);
}
}
self.table.entry_in_set(v, self.key.set)
}
fn build(mut self) -> Barcode<'table> {
let mut total = 0;
for (mut idx, character) in self.code.iter().enumerate() {
if idx == 0 {
idx = 1;
}
total += character.value() as usize * idx
}
total %= 103;
self.code.push(
self.table
.entries
.iter()
.find(|v| v.value() as usize == total)
.unwrap(),
);
self.code.push(
self.table
.entry_in_set(BarcodeValue::Stop, self.key.set)
.unwrap(),
);
Barcode { code: self.code }
}
fn key(&self) -> BarcodeKey<'s> {
self.key
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BarcodeKey<'s> {
remaining: &'s str,
set: BarcodeType,
fnc4: bool,
}
impl<'s> BarcodeKey<'s> {
pub fn new(text: &'s str, set: BarcodeType) -> Self {
Self {
remaining: text,
set,
fnc4: false,
}
}
pub fn with_fnc(mut self, fnc4: bool) -> Self {
self.fnc4 = fnc4;
self
}
pub fn is_empty(&self) -> bool {
self.remaining.is_empty()
}
}