use super::helpers::{vec, Vec};
use crate::error::Result;
use crate::sym::{helpers, Parse};
use core::ops::Range;
const CHARS: [(char, [u8; 9]); 47] = [
('0', [1, 0, 0, 0, 1, 0, 1, 0, 0]),
('1', [1, 0, 1, 0, 0, 1, 0, 0, 0]),
('2', [1, 0, 1, 0, 0, 0, 1, 0, 0]),
('3', [1, 0, 1, 0, 0, 0, 0, 1, 0]),
('4', [1, 0, 0, 1, 0, 1, 0, 0, 0]),
('5', [1, 0, 0, 1, 0, 0, 1, 0, 0]),
('6', [1, 0, 0, 1, 0, 0, 0, 1, 0]),
('7', [1, 0, 1, 0, 1, 0, 0, 0, 0]),
('8', [1, 0, 0, 0, 1, 0, 0, 1, 0]),
('9', [1, 0, 0, 0, 0, 1, 0, 1, 0]),
('A', [1, 1, 0, 1, 0, 1, 0, 0, 0]),
('B', [1, 1, 0, 1, 0, 0, 1, 0, 0]),
('C', [1, 1, 0, 1, 0, 0, 0, 1, 0]),
('D', [1, 1, 0, 0, 1, 0, 1, 0, 0]),
('E', [1, 1, 0, 0, 1, 0, 0, 1, 0]),
('F', [1, 1, 0, 0, 0, 1, 0, 1, 0]),
('G', [1, 0, 1, 1, 0, 1, 0, 0, 0]),
('H', [1, 0, 1, 1, 0, 0, 1, 0, 0]),
('I', [1, 0, 1, 1, 0, 0, 0, 1, 0]),
('J', [1, 0, 0, 1, 1, 0, 1, 0, 0]),
('K', [1, 0, 0, 0, 1, 1, 0, 1, 0]),
('L', [1, 0, 1, 0, 1, 1, 0, 0, 0]),
('M', [1, 0, 1, 0, 0, 1, 1, 0, 0]),
('N', [1, 0, 1, 0, 0, 0, 1, 1, 0]),
('O', [1, 0, 0, 1, 0, 1, 1, 0, 0]),
('P', [1, 0, 0, 0, 1, 0, 1, 1, 0]),
('Q', [1, 1, 0, 1, 1, 0, 1, 0, 0]),
('R', [1, 1, 0, 1, 1, 0, 0, 1, 0]),
('S', [1, 1, 0, 1, 0, 1, 1, 0, 0]),
('T', [1, 1, 0, 1, 0, 0, 1, 1, 0]),
('U', [1, 1, 0, 0, 1, 0, 1, 1, 0]),
('V', [1, 1, 0, 0, 1, 1, 0, 1, 0]),
('W', [1, 0, 1, 1, 0, 1, 1, 0, 0]),
('X', [1, 0, 1, 1, 0, 0, 1, 1, 0]),
('Y', [1, 0, 0, 1, 1, 0, 1, 1, 0]),
('Z', [1, 0, 0, 1, 1, 1, 0, 1, 0]),
('-', [1, 0, 0, 1, 0, 1, 1, 1, 0]),
('.', [1, 1, 1, 0, 1, 0, 1, 0, 0]),
(' ', [1, 1, 1, 0, 1, 0, 0, 1, 0]),
('$', [1, 1, 1, 0, 0, 1, 0, 1, 0]),
('/', [1, 0, 1, 1, 0, 1, 1, 1, 0]),
('+', [1, 0, 1, 1, 1, 0, 1, 1, 0]),
('%', [1, 1, 0, 1, 0, 1, 1, 1, 0]),
('(', [1, 0, 0, 1, 0, 0, 1, 1, 0]),
(')', [1, 1, 1, 0, 1, 1, 0, 1, 0]),
('[', [1, 1, 1, 0, 1, 0, 1, 1, 0]),
(']', [1, 0, 0, 1, 1, 0, 0, 1, 0]),
];
const GUARD: [u8; 9] = [1, 0, 1, 0, 1, 1, 1, 1, 0];
const TERMINATOR: [u8; 1] = [1];
#[derive(Debug)]
pub struct Code93(Vec<char>);
impl Code93 {
pub fn new<T: AsRef<str>>(data: T) -> Result<Self> {
Self::parse(data.as_ref()).map(|d| Self(d.chars().collect()))
}
pub(crate) fn char_encoding(c: char) -> [u8; 9] {
match CHARS.iter().find(|&ch| ch.0 == c) {
Some(&(_, enc)) => enc,
None => panic!("Unknown char: {c}"),
}
}
pub(crate) fn checksum_char(data: &[char], weight_threshold: usize) -> Option<char> {
let get_char_pos = |&c| {
CHARS
.iter()
.position(|t| t.0 == c)
.expect("Character not found in CHARS mapping")
};
let weight = |i| match (data.len() - i) % weight_threshold {
0 => weight_threshold,
n => n,
};
let positions = data.iter().map(get_char_pos);
let index = positions
.enumerate()
.fold(0, |acc, (i, pos)| acc + (weight(i) * pos));
CHARS.get(index % CHARS.len()).map(|&(c, _)| c)
}
pub(crate) fn c_checksum_char(data: &[char]) -> Option<char> {
Self::checksum_char(data, 20)
}
pub(crate) fn k_checksum_char(data: &[char], c_checksum: char) -> Option<char> {
let mut extended_data: Vec<char> = data.to_vec();
extended_data.push(c_checksum);
Self::checksum_char(&extended_data, 15)
}
fn push_encoding(into: &mut Vec<u8>, from: [u8; 9]) {
into.extend(from.iter().copied());
}
fn payload(&self) -> Vec<u8> {
let mut enc = vec![];
let c_checksum = Self::c_checksum_char(&self.0).expect("Cannot compute checksum C");
let k_checksum =
Self::k_checksum_char(&self.0, c_checksum).expect("Cannot compute checksum K");
for &c in &self.0 {
Self::push_encoding(&mut enc, Self::char_encoding(c));
}
Self::push_encoding(&mut enc, Self::char_encoding(c_checksum));
Self::push_encoding(&mut enc, Self::char_encoding(k_checksum));
enc
}
#[must_use]
pub fn encode(&self) -> Vec<u8> {
let guard = &GUARD[..];
let terminator = &TERMINATOR[..];
helpers::join_slices(&[guard, &self.payload()[..], guard, terminator][..])
}
}
impl Parse for Code93 {
fn valid_len() -> Range<u32> {
1..256
}
fn valid_chars() -> Vec<char> {
let (chars, _): (Vec<_>, Vec<_>) = CHARS.iter().copied().unzip();
chars
}
}
#[cfg(test)]
mod tests {
use crate::error::Error;
use crate::sym::code93::*;
#[cfg(not(feature = "std"))]
use alloc::string::String;
use core::char;
fn collapse_vec(v: &[u8]) -> String {
let chars = v.iter().map(|d| {
char::from_digit(u32::from(*d), 10).expect("Failed to convert digit to character")
});
chars.collect()
}
#[test]
fn invalid_length_code93() {
let code93 = Code93::new("");
assert!(code93.is_err());
if let Err(error) = code93 {
assert_eq!(error, Error::Length);
}
}
#[test]
fn invalid_data_code93() {
let code93 = Code93::new("lowerCASE");
assert!(code93.is_err());
if let Err(error) = code93 {
assert_eq!(error, Error::Character);
}
}
#[test]
fn code93_encode() {
let code931 = Code93::new("TEST93").expect("Failed to create Code93 for 'TEST93'");
let code932 = Code93::new("FLAM").expect("Failed to create Code93 for 'FLAM'");
let code933 = Code93::new("99").expect("Failed to create Code93 for '99'");
let code934 =
Code93::new("1111111111111111111111").expect("Failed to create Code93 for long input");
assert_eq!(collapse_vec(&code931.encode()), "1010111101101001101100100101101011001101001101000010101010000101011101101001000101010111101");
assert_eq!(
collapse_vec(&code932.encode()),
"1010111101100010101010110001101010001010011001001011001010011001010111101"
);
assert_eq!(
collapse_vec(&code933.encode()),
"1010111101000010101000010101101100101000101101010111101"
);
assert_eq!(collapse_vec(&code934.encode()), "1010111101010010001010010001010010001010010001010010001010010001010010001010010001010010001010010001010010001010010001010010001010010001010010001010010001010010001010010001010010001010010001010010001010010001000101101110010101010111101");
}
}