use bitvec::order::Msb0;
use bitvec::vec::BitVec;
use crate::qrcode::QRGenError;
use super::bits::push_bits;
pub type EncodeBits = BitVec<u8, Msb0>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Mode {
Numeric,
Alphanumeric,
Byte,
}
impl Mode {
pub fn can_encode(&self, input: &str) -> bool {
match self {
Mode::Numeric => input.chars().all(|c| c.is_ascii_digit()),
Mode::Alphanumeric => input.chars().all(Self::is_alphanumeric_char),
Mode::Byte => true, }
}
fn is_alphanumeric_char(c: char) -> bool {
matches!(c, '0'..='9' | 'A'..='Z' | ' ' | '$' | '%' | '*' | '+' | '-' | '.' | '/' | ':')
}
pub fn indicator_bits(&self) -> u8 {
match self {
Mode::Numeric => 0b0001,
Mode::Alphanumeric => 0b0010,
Mode::Byte => 0b0100,
}
}
}
pub fn char_count_bits(mode: Mode, version: u8) -> usize {
let version = version.max(1);
let group: usize = match version {
1..=9 => 0,
10..=26 => 1,
_ => 2,
};
match (mode, group) {
(Mode::Numeric, 0) => 10,
(Mode::Numeric, 1) => 12,
(Mode::Numeric, 2) => 14,
(Mode::Alphanumeric, 0) => 9,
(Mode::Alphanumeric, 1) => 11,
(Mode::Alphanumeric, 2) => 13,
(Mode::Byte, 0) => 8,
(Mode::Byte, 1) => 16,
(Mode::Byte, 2) => 16,
_ => unreachable!(),
}
}
pub fn estimated_bit_length(input: &str, mode: Mode, version: u8) -> usize {
4 + char_count_bits(mode, version) + mode.data_bits_for_string(input)
}
impl Mode {
pub fn data_bits_for_string(&self, input: &str) -> usize {
match self {
Mode::Numeric => crate::encoder::numeric::bit_length(input),
Mode::Alphanumeric => crate::encoder::alphanumeric::bit_length(input),
Mode::Byte => input.len() * 8,
}
}
}
pub fn best_mode(input: &str, version: u8) -> Mode {
let candidates = [Mode::Numeric, Mode::Alphanumeric, Mode::Byte];
let mut best = (Mode::Byte, usize::MAX);
for mode in candidates {
if !mode.can_encode(input) {
continue;
}
let bits = estimated_bit_length(input, mode, version);
if bits < best.1 {
best = (mode, bits);
}
}
best.0
}
pub struct Encoder;
impl Encoder {
pub fn encode(input: &str, version: u8) -> Result<EncodeBits, QRGenError> {
let mode = best_mode(input, version);
Self::encode_with_mode(input, mode, version)
}
pub fn encode_with_mode(input: &str, mode: Mode, version: u8) -> Result<EncodeBits, QRGenError> {
let mut bits = EncodeBits::new();
push_bits(&mut bits, mode.indicator_bits() as u32, 4);
let count = input.len() as u32;
let cc_bits = char_count_bits(mode, version);
push_bits(&mut bits, count, cc_bits);
match mode {
Mode::Numeric => crate::encoder::numeric::encode(input, &mut bits),
Mode::Alphanumeric => crate::encoder::alphanumeric::encode(input, &mut bits),
Mode::Byte => crate::encoder::byte::encode(input, &mut bits),
}
Ok(bits)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_numeric_only() {
assert!(Mode::Numeric.can_encode("123456"));
assert!(!Mode::Numeric.can_encode("AB12"));
assert!(Mode::Alphanumeric.can_encode("AB12"));
}
#[test]
fn test_alphanumeric_special_chars() {
assert!(Mode::Alphanumeric.can_encode("A+B"));
assert!(Mode::Alphanumeric.can_encode("1-3"));
assert!(Mode::Alphanumeric.can_encode("AB")); assert!(!Mode::Alphanumeric.can_encode("a+b")); }
#[test]
fn test_byte_catchall() {
assert!(Mode::Byte.can_encode("anything"));
assert!(Mode::Byte.can_encode("日本語"));
}
#[test]
fn test_mode_indicator_bits() {
assert_eq!(Mode::Numeric.indicator_bits(), 0b0001);
assert_eq!(Mode::Alphanumeric.indicator_bits(), 0b0010);
assert_eq!(Mode::Byte.indicator_bits(), 0b0100);
}
#[test]
fn test_char_count_bits_by_version_group() {
assert_eq!(char_count_bits(Mode::Byte, 1), 8);
assert_eq!(char_count_bits(Mode::Alphanumeric, 1), 9);
assert_eq!(char_count_bits(Mode::Numeric, 1), 10);
assert_eq!(char_count_bits(Mode::Byte, 10), 16);
assert_eq!(char_count_bits(Mode::Alphanumeric, 10), 11);
assert_eq!(char_count_bits(Mode::Numeric, 10), 12);
assert_eq!(char_count_bits(Mode::Byte, 27), 16);
assert_eq!(char_count_bits(Mode::Alphanumeric, 27), 13);
assert_eq!(char_count_bits(Mode::Numeric, 27), 14);
}
}