#![no_std]
#[cfg(not(feature = "std"))]
extern crate alloc as std;
#[cfg(feature = "std")]
extern crate std;
#[cfg(test)]
use std::vec;
use std::vec::Vec;
mod decode;
mod encode;
mod latin1;
#[cfg(feature = "unicode")]
mod unicode;
pub use decode::{decode, decode_str, DecodingError};
#[cfg(feature = "unicode")]
pub use unicode::bars_to_blocks;
const SHIFT_MODE: u8 = 98;
const SWITCH_C: u8 = 99;
const SWITCH_B: u8 = 100;
const SWITCH_A: u8 = 101;
const START_A: u8 = 103;
const START_B: u8 = 104;
const START_C: u8 = 105;
const STOP: u8 = 108;
fn checksum(symbols: impl Iterator<Item = u8>) -> u8 {
(symbols
.enumerate()
.map(|(i, idx)| (i.max(1) as u64) * idx as u64)
.sum::<u64>()
% 103) as u8
}
#[derive(PartialEq, Debug, Clone, Copy)]
pub struct Bar {
pub width: u8,
pub space: u8,
}
#[derive(PartialEq, Debug, Clone, Copy)]
pub struct BarCoordinate {
pub x: u32,
pub width: u8,
}
pub struct Code128 {
indices: Vec<u8>,
}
impl Code128 {
pub fn encode(data: &[u8]) -> Self {
Code128Builder::default().encode(data)
}
pub fn encode_str(text: &str) -> Option<Self> {
Code128Builder::default().encode_str(text)
}
pub fn bars(&self) -> impl Iterator<Item = Bar> + '_ {
self.indices
.iter()
.flat_map(|idx| encode::bits_to_bars(encode::PATTERNS[*idx as usize]))
}
pub fn bar_coordinates(&self) -> impl Iterator<Item = BarCoordinate> + '_ {
self.bars().scan(10, |pos, bar| {
let x = *pos;
*pos += bar.width as u32 + bar.space as u32;
Some(BarCoordinate {
x,
width: bar.width,
})
})
}
pub fn len(&self) -> usize {
self.indices.len() * 11 + 2 + 20
}
pub fn is_empty(&self) -> bool {
self.indices.len() == 3
}
}
#[doc(hidden)]
#[derive(Copy, Clone)]
pub enum Encoder {
Mixed,
DynamicProgramming,
}
#[doc(hidden)]
pub struct Code128Builder {
encoder: Encoder,
}
impl Code128Builder {
pub fn with_encoder(self, encoder: Encoder) -> Self {
Self { encoder }
}
pub fn encode(self, data: &[u8]) -> Code128 {
let mut indices = match self.encoder {
Encoder::Mixed => encode::encode_as_indices(data),
Encoder::DynamicProgramming => encode::encode_as_indices_dp(data, Vec::new()),
};
indices.push(checksum(indices.iter().cloned()));
indices.push(STOP);
Code128 { indices }
}
pub fn encode_str(self, text: &str) -> Option<Code128> {
latin1::utf8_to_latin1(text).map(|data| self.encode(&data))
}
}
#[allow(clippy::derivable_impls)]
impl Default for Code128Builder {
fn default() -> Self {
Self {
encoder: Encoder::Mixed,
}
}
}
#[test]
fn test_bar_size() {
for pattern in &encode::PATTERNS[0..107] {
let size: u32 = encode::bits_to_bars(*pattern)
.into_iter()
.map(|m| m.width as u32 + m.space as u32)
.sum();
assert_eq!(size, 11);
}
let size: u32 = encode::bits_to_bars(encode::PATTERNS[STOP as usize])
.into_iter()
.map(|m| m.width as u32 + m.space as u32)
.sum();
assert_eq!(size, 13);
}
#[test]
fn test_code_size() {
let code = Code128::encode(b"foo");
let size = code
.bars()
.map(|m| m.width as u32 + m.space as u32)
.sum::<u32>()
+ 20;
assert_eq!(code.len(), size as usize);
}
#[test]
fn test_is_empty() {
assert!(Code128::encode(b"").is_empty());
assert!(!Code128::encode(b".").is_empty());
}
#[test]
fn test_256() {
for x in 0..=255 {
let code = Code128::encode(&[x]);
let bars: Vec<Bar> = code.bars().collect();
assert_eq!(decode(&bars), Ok(vec![x]));
}
}
#[test]
fn test_string_encoding_example() {
let code = Code128::encode_str("Füße").unwrap();
let bars: Vec<_> = code.bars().collect();
assert_eq!(decode_str(&bars), Ok("Füße".into()));
}
#[test]
fn test_bar_coordinates() {
let code = Code128::encode(b"");
let bars: Vec<_> = code.bar_coordinates().collect();
assert_eq!(bars[0], BarCoordinate { x: 10, width: 2 });
assert_eq!(bars[1], BarCoordinate { x: 13, width: 1 });
assert_eq!(bars[2], BarCoordinate { x: 16, width: 1 });
assert_eq!(bars[3], BarCoordinate { x: 21, width: 2 });
assert_eq!(bars[4], BarCoordinate { x: 25, width: 2 });
assert_eq!(bars[5], BarCoordinate { x: 28, width: 2 });
assert_eq!(bars[6], BarCoordinate { x: 32, width: 2 });
assert_eq!(bars[7], BarCoordinate { x: 37, width: 3 });
assert_eq!(bars[8], BarCoordinate { x: 41, width: 1 });
assert_eq!(bars[9], BarCoordinate { x: 43, width: 2 });
assert_eq!(bars.len(), 10);
}