use anyd::codes::dotcode::{DotCodeDecoder, DotCodeEncoder};
use anyd::output::Encoding;
use anyd::symbol::SymbolMeta;
use anyd::symbology::Symbology;
use anyd::traits::{Decode, Encode};
fn matrix_rows(encoding: &Encoding) -> Vec<String> {
match encoding {
Encoding::Matrix(m) => (0..m.height())
.map(|y| {
(0..m.width())
.map(|x| if m.get(x, y) { '1' } else { '0' })
.collect()
})
.collect(),
Encoding::Linear(_) => panic!("DotCode must produce a matrix"),
}
}
#[test]
fn reference_bitmap_rev4_figure6() {
let expected = [
"10000000001010001000101",
"01010101000100000101000",
"00100010000000100000001",
"01010001000001000001000",
"10101010100000001010101",
"00000100010100000100010",
"00000000001010101010001",
"00010001010001000001000",
"00101010101000001010001",
"01000100000001010000000",
"10101000101000101000001",
"00010101000100010101010",
"10001000001010100000101",
"01010001010001000001010",
"10000010101010100010101",
"01000101000101010101010",
];
let encoder = DotCodeEncoder::new();
let symbol = encoder.build_gs1_reduced(b"1707062010ABC123456").unwrap();
let encoding = encoder.encode(&symbol).unwrap();
assert_eq!(matrix_rows(&encoding), expected);
}
fn assert_roundtrip(symbol: &anyd::Symbol) {
let encoder = DotCodeEncoder::new();
let decoder = DotCodeDecoder::new();
let encoding = encoder.encode(symbol).unwrap();
let decoded = decoder.decode(&encoding).unwrap();
let reencoded = encoder.encode(&decoded).unwrap();
assert_eq!(reencoded, encoding, "re-encoded matrix differs");
assert_eq!(decoded.symbology, Symbology::DotCode);
match (&symbol.meta, &decoded.meta) {
(SymbolMeta::DotCode(a), SymbolMeta::DotCode(b)) => assert_eq!(a, b, "meta differs"),
_ => panic!("expected DotCodeMeta"),
}
}
#[test]
fn roundtrip_across_code_sets_and_sizes() {
let encoder = DotCodeEncoder::new();
let cases: &[&[u8]] = &[
b"A",
b"AB",
b"Hello, World!",
b"ABCDE12345678",
b"1234567890",
b"1712345610",
b"00",
b"\x00ABCD1234567890",
b"99aA[{00\x00",
b"The quick brown fox jumps over the lazy dog 0123456789",
b"lower case letters and DIGITS 42 mixed",
b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
b"9999999999999999999999999999999999999999",
];
for &data in cases {
let symbol = encoder.build_bytes(data).unwrap();
assert_roundtrip(&symbol);
let decoded = DotCodeDecoder::new()
.decode(&encoder.encode(&symbol).unwrap())
.unwrap();
assert_eq!(
decoded.payload_bytes(),
data,
"payload mismatch for {data:?}"
);
}
}
#[test]
fn roundtrip_binary_and_high_bytes() {
let encoder = DotCodeEncoder::new();
let cases: Vec<Vec<u8>> = vec![
vec![0x80, 0x81, 0x82, 0x83, 0x31, 0x32, 0x33, 0x34],
b"abcde\x80\x81\x82\x83\x84\xff".to_vec(),
(0u16..=255).map(|b| b as u8).collect(),
vec![0xff; 40],
b"text then binary \x80\x90\xa0\xb0 and more".to_vec(),
];
for data in &cases {
let symbol = encoder.build_bytes(data).unwrap();
assert_roundtrip(&symbol);
let decoded = DotCodeDecoder::new()
.decode(&encoder.encode(&symbol).unwrap())
.unwrap();
assert_eq!(decoded.payload_bytes(), *data, "binary payload mismatch");
}
}
#[test]
fn roundtrip_user_width() {
let encoder = DotCodeEncoder::new();
for width in [7usize, 13, 20, 30] {
let symbol = encoder
.build_bytes_width(b"WIDTH TEST 12345", width)
.unwrap();
assert_roundtrip(&symbol);
}
}
#[test]
fn roundtrip_gs1_reduced() {
let encoder = DotCodeEncoder::new();
let symbol = encoder.build_gs1_reduced(b"1707062010ABC123456").unwrap();
assert_roundtrip(&symbol);
}
#[test]
fn rejects_non_dotcode_symbol() {
let encoder = DotCodeEncoder::new();
let symbol = encoder.build_bytes(b"CHECKSUM").unwrap();
let mut encoding = encoder.encode(&symbol).unwrap();
if let Encoding::Matrix(m) = &mut encoding {
let w = m.width();
for y in 0..m.height() {
for x in 0..w {
if (x + y) % 2 == 0 {
m.set(x, y, !m.get(x, y));
}
}
}
}
assert!(DotCodeDecoder::new().decode(&encoding).is_err());
}