use crate::{bit_buffer::BitBuffer, qr_spec::{get_qr_spec, EcLevel, QrSpec}};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Mode {
Numeric = 0b0001,
Alphanumeric = 0b0010,
Byte = 0b0100,
}
impl Mode {
pub fn mode_indicator(&self) -> u32 {
*self as u32
}
pub fn char_count_bits(&self) -> usize {
match self {
Mode::Numeric => 10,
Mode::Alphanumeric => 9,
Mode::Byte => 8,
}
}
}
fn is_alphanumeric_qr_char(c: char) -> bool {
matches!(c, '0'..='9' | 'A'..='Z' | ' ' | '$' | '%' | '*' | '+' | '-' | '.' | '/' | ':')
}
fn get_alphanumeric_value(c: char) -> u32 {
match c {
'0'..='9' => (c as u32) - ('0' as u32),
'A'..='Z' => (c as u32) - ('A' as u32) + 10,
' ' => 36,
'$' => 37,
'%' => 38,
'*' => 39,
'+' => 40,
'-' => 41,
'.' => 42,
'/' => 43,
':' => 44,
_ => panic!("Invalid QR alphanumeric character"),
}
}
pub fn pad_buf_bits(buf: &mut BitBuffer, target_byte_capacity: usize) {
let target_bits = target_byte_capacity * 8;
let terminator_len = (target_bits.saturating_sub(buf.len_bits())).min(4);
buf.push_bits(0, terminator_len);
let remainder = buf.len_bits() % 8;
if remainder != 0 {
buf.push_bits(0, 8 - remainder);
}
let mut pad_byte = 0b1110_1100; while buf.len_bits() < target_bits {
buf.push_bits(pad_byte, 8);
pad_byte = if pad_byte == 0b1110_1100 {
0b0001_0001 } else {
0b1110_1100 };
}
}
pub fn detect_mode(text: &str) -> Mode {
if text.chars().all(|c| c.is_ascii_digit()) {
Mode::Numeric
} else if text.chars().all(is_alphanumeric_qr_char) {
Mode::Alphanumeric
} else {
Mode::Byte
}
}
pub fn encode(text: &str, mode: Mode, spec: QrSpec) -> BitBuffer {
let mut buf = BitBuffer::new();
buf.push_bits(mode.mode_indicator(), 4);
let char_count = text.chars().count();
buf.push_bits(char_count as u32, spec.char_count_bits(mode));
match mode {
Mode::Numeric => encode_numeric(text, &mut buf),
Mode::Alphanumeric => encode_alphanumeric(text, &mut buf),
Mode::Byte => encode_byte(text, &mut buf),
}
buf
}
fn encode_numeric(text: &str, buf: &mut BitBuffer) {
let bytes = text.as_bytes();
let mut i = 0;
while i < bytes.len() {
if i+3 <= bytes.len() { let val = (bytes[i] - b'0') as u32 * 100
+ (bytes[i+1] - b'0') as u32 * 10
+ (bytes[i+2] - b'0') as u32;
buf.push_bits(val, 10);
i += 3;
} else if i+2 == bytes.len() { let val = (bytes[i] - b'0') as u32 * 10
+ (bytes[i+1] - b'0') as u32;
buf.push_bits(val, 7);
i += 2;
} else { let val = (bytes[i] - b'0') as u32;
buf.push_bits(val, 4);
i += 1;
}
}
}
fn encode_alphanumeric(text: &str, buf: &mut BitBuffer) {
let chars: Vec<char> = text.chars().collect();
let mut i = 0;
while i < chars.len() {
if i+2 <= chars.len() { let val = get_alphanumeric_value(chars[i]) * 45 + get_alphanumeric_value(chars[i+1]);
buf.push_bits(val, 11);
i += 2;
} else { let val = get_alphanumeric_value(chars[i]);
buf.push_bits(val, 6);
i += 1;
}
}
}
fn encode_byte(text: &str, buf: &mut BitBuffer) {
for byte in text.bytes() {
buf.push_bits(byte as u32, 8);
}
}
pub fn prepare_data(text: &str, ec_level: Option<EcLevel>) -> Result<(BitBuffer, QrSpec), &'static str> {
let ec = ec_level.unwrap_or(EcLevel::M);
let mode = detect_mode(text);
let char_count = text.chars().count();
let mut spec: Option<QrSpec> = None;
for version in 1..=10 {
let cur_spec = get_qr_spec(version, ec);
let header_bits = 4 + cur_spec.char_count_bits(mode);
let payload_bits = match mode {
Mode::Numeric => (char_count / 3) * 10 + match char_count % 3 { 2 => 7, 1 => 4, _ => 0},
Mode::Alphanumeric => (char_count / 2) * 11 + (char_count % 2) * 6,
Mode::Byte => text.len() * 8,
};
if header_bits + payload_bits <= cur_spec.data_codewords * 8 {
spec = Some(cur_spec);
break;
}
}
let spec = spec.ok_or("Payload too large for versions 1-10")?;
let mut buf = encode(text, mode, spec);
pad_buf_bits(&mut buf, spec.data_codewords);
Ok((buf, spec))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_prepare_data_hello() {
let (buf, spec) = prepare_data("1028931", None).unwrap();
assert_eq!(spec.version, 1);
assert_eq!(spec.ec_level, EcLevel::M);
assert_eq!(spec.data_codewords, 16);
assert_eq!(buf.len_bits(), 128);
let bytes = buf.to_bytes(); assert_eq!(bytes.len(), 16);
}
}