use crate::util::qr::QRError;
use crate::util::Chomp;
pub fn data(input: Vec<u8>, version: u32) -> Result<String, QRError> {
let mut chomp = Chomp::new(input);
let mut result = String::new();
while let Some(mode) = chomp.chomp(4) {
match mode {
0b0001 => result.push_str(numeric(&mut chomp, version)?.as_str()),
0b0010 => result.push_str(alphanumeric(&mut chomp, version)?.as_str()),
0b0100 => result.push_str(eight_bit(&mut chomp, version)?.as_str()),
0b0000 => break,
_ => {
return Err(QRError {
msg: format!("Mode {:04b} not yet implemented.", mode),
})
}
}
}
Ok(result)
}
fn numeric(chomp: &mut Chomp, version: u32) -> Result<String, QRError> {
let length_bits = match version {
1..=9 => 10,
10..=26 => 12,
27..=40 => 14,
_ => {
return Err(QRError {
msg: format!("Unknown version {}", version),
});
}
};
let mut length = chomp.chomp_or_u16(
length_bits,
QRError {
msg: format!("Could not read {} bits for numeric length", length_bits),
},
)?;
let mut result = String::new();
while length > 0 {
if length >= 3 {
let digits = read_bits_u16(chomp, 10)?;
result.push_str(&format!("{:03}", digits));
length -= 3;
continue;
}
if length == 2 {
let digits = read_bits_u16(chomp, 7)?;
result.push_str(&format!("{:02}", digits));
break;
}
if length == 1 {
let digits = read_bits_u16(chomp, 4)?;
result.push_str(&format!("{:01}", digits));
break;
}
}
debug!("NUMERIC {:?}", result);
Ok(result)
}
const ALPHANUMERIC: [char; 45] = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '$',
'%', '*', '+', '-', '.', '/', ':',
];
fn alphanumeric(chomp: &mut Chomp, version: u32) -> Result<String, QRError> {
let length_bits = match version {
1..=9 => 9,
10..=26 => 11,
27..=40 => 13,
_ => {
return Err(QRError {
msg: format!("Unknown version {}", version),
});
}
};
let mut length = chomp.chomp_or_u16(
length_bits,
QRError {
msg: format!(
"Could not read {} bits for alphanumeric length",
length_bits
),
},
)?;
let mut result = String::new();
while length > 0 {
if length >= 2 {
let chars = read_bits_u16(chomp, 11)?;
let char1 = chars as usize / 45;
let char2 = chars as usize % 45;
if char1 > 44 { return Err(QRError {
msg: format!("Invalid character in alphanumeric data {}", char1),
});
}
result.push(ALPHANUMERIC[char1]);
result.push(ALPHANUMERIC[char2]);
length -= 2;
continue;
}
if length == 1 {
let chars = read_bits_u16(chomp, 6)?;
result.push(ALPHANUMERIC[chars as usize]);
break;
}
}
debug!("ALPHANUMERIC {:?}", result);
Ok(result)
}
fn eight_bit(chomp: &mut Chomp, version: u32) -> Result<String, QRError> {
let length_bits = match version {
1..=9 => 8,
10..=26 => 16,
27..=40 => 16,
_ => {
return Err(QRError {
msg: format!("Unknown version {}", version),
});
}
};
let length = chomp.chomp_or_u16(
length_bits,
QRError {
msg: format!(
"Could not read {} bits for alphanumeric length",
length_bits
),
},
)?;
let mut result = vec![];
for _ in 0..length {
result.push(read_bits(chomp, 8)?);
}
debug!("EIGHT BIT RAW {:?}", result);
let mut may_be_utf8 = false;
for r in &result {
if *r == 0xC3 {
may_be_utf8 = true;
break;
}
}
let final_result = if may_be_utf8 {
let utf8 = String::from_utf8(result)?;
debug!("EIGHT BIT AS UTF-8 {:?}", utf8);
utf8
} else {
let mut iso88591 = String::new();
for r in result {
iso88591.push(r as char);
}
debug!("EIGHT BIT AS ISO 8859-1 {:?}", iso88591);
iso88591
};
Ok(final_result)
}
fn read_bits(chomp: &mut Chomp, bits: u8) -> Result<u8, QRError> {
chomp.chomp_or(
bits,
QRError {
msg: format!("Could not read {} bits", bits),
},
)
}
fn read_bits_u16(chomp: &mut Chomp, bits: u8) -> Result<u16, QRError> {
chomp.chomp_or_u16(
bits,
QRError {
msg: format!("Could not read {} bits", bits),
},
)
}