use anyd::Symbology;
use anyd::codes::postal::{PostalDecoder, PostalEncoder, PostalVariant};
use anyd::output::{BitMatrix, Encoding};
use anyd::symbol::{Symbol, SymbolMeta};
use anyd::traits::{Decode, Encode};
fn bar_letters(symbol: &Symbol) -> String {
let matrix = match PostalEncoder::new().encode(symbol).unwrap() {
Encoding::Matrix(m) => m,
Encoding::Linear(_) => panic!("expected a matrix"),
};
matrix_letters(&matrix)
}
fn matrix_letters(m: &BitMatrix) -> String {
assert_eq!(m.height(), 3, "postal matrix must be 3 rows");
let n = m.width().div_ceil(2);
(0..n)
.map(|i| {
let c = i * 2;
assert!(m.get(c, 1), "tracker band must be dark");
match (m.get(c, 0), m.get(c, 2)) {
(true, true) => 'F',
(true, false) => 'A',
(false, true) => 'D',
(false, false) => 'T',
}
})
.collect()
}
fn variant_of(symbol: &Symbol) -> PostalVariant {
match &symbol.meta {
SymbolMeta::Postal(m) => m.variant,
_ => panic!("not a postal symbol"),
}
}
fn assert_lossless(symbol: &Symbol) {
let enc = PostalEncoder::new();
let encoding = enc.encode(symbol).unwrap();
let decoded = PostalDecoder::new().decode(&encoding).unwrap();
assert_eq!(decoded.symbology, symbol.symbology, "symbology differs");
assert_eq!(decoded.segments, symbol.segments, "segments differ");
assert_eq!(decoded.meta, symbol.meta, "meta differs");
assert_eq!(
enc.encode(&decoded).unwrap(),
encoding,
"re-encoding not identical"
);
}
#[test]
fn auspost_reference_standard_diagram1() {
let sym = PostalEncoder::new().build_auspost("96184209", "").unwrap();
let bars = bar_letters(&sym);
assert_eq!(bars.len(), 37, "Standard Customer Barcode is 37 bars");
assert_eq!(bars, "ATFAFATFDFFADDAAFDFFTFTDFFTDADADTADAT");
assert!(bars.starts_with("AT") && bars.ends_with("AT"));
}
#[test]
fn auspost_reference_guide_figure3() {
let sym = PostalEncoder::new().build_auspost("39549554", "").unwrap();
assert_eq!(bar_letters(&sym), "ATFAFAAFTFADAATFADADAATTADAFATAATDDAT");
}
#[test]
fn auspost_reference_guide_figure4_customer2() {
let sym = PostalEncoder::new()
.build_auspost("56439111", "ABA 9")
.unwrap();
let bars = bar_letters(&sym);
assert_eq!(bars.len(), 52, "Customer Barcode 2 is 52 bars");
assert_eq!(bars, "ATADTFADDFAAAFTFFAFAFAFFFFFAFFFFFTTDDTTAFADAFFFTTAAT");
}
#[test]
fn auspost_roundtrip_formats() {
let enc = PostalEncoder::new();
let cases = [
("96184209", ""),
("56439111", "ABA 9"),
("12345678", "9012345"),
("12345678", "abc#12"),
("00000000", ""),
("1234", ""),
];
for (dpid, custinfo) in cases {
let sym = enc.build_auspost(dpid, custinfo).unwrap();
assert_eq!(variant_of(&sym), PostalVariant::AustraliaPost);
assert_eq!(sym.symbology, Symbology::AustraliaPost);
assert!(matches!(bar_letters(&sym).len(), 37 | 52 | 67));
assert_lossless(&sym);
}
}
#[test]
fn auspost_short_dpid_is_zero_padded() {
let enc = PostalEncoder::new();
let sym = enc.build_auspost("1234", "").unwrap();
let decoded = PostalDecoder::new()
.decode(&enc.encode(&sym).unwrap())
.unwrap();
assert_eq!(decoded.segments[0].data, b"00001234");
}
#[test]
fn auspost_rejects_bad_fields() {
let enc = PostalEncoder::new();
assert!(enc.build_auspost("123456789", "").is_err()); assert!(enc.build_auspost("12A45", "").is_err()); assert!(enc.build_auspost("12345678", "\u{00e9}").is_err()); assert!(enc.build_auspost("12345678", "1234567890123456").is_err());
}
#[test]
fn japanpost_reference_manual_example1() {
let sym = PostalEncoder::new()
.build_japanpost("15400233-16-4-205")
.unwrap();
let bars = bar_letters(&sym);
assert_eq!(bars.len(), 67, "Japan Post is always 67 bars");
assert_eq!(
bars,
"FDFFTFTFFADFTTFTTFDADFADFATFTFFTDAFTFTFADTFTFDAFTTFTFTDATDATDADAFDF"
);
assert!(bars.starts_with("FD") && bars.ends_with("DF"));
}
#[test]
fn japanpost_reference_manual_example2() {
let sym = PostalEncoder::new()
.build_japanpost("350110622-1A308")
.unwrap();
assert_eq!(
bar_letters(&sym),
"FDDFAFTFFTTFFTFFTFTTDAFFDAFDATFTFFTDATFTTDFAFTTADFTDATDATDATDAFTFDF"
);
}
#[test]
fn japanpost_roundtrip() {
let enc = PostalEncoder::new();
for data in [
"15400233-16-4-205",
"350110622-1A308",
"1234",
"123456-AB",
"ABCDEFGHIJ",
"1234567890ABCDE",
] {
let sym = enc.build_japanpost(data).unwrap();
assert_eq!(variant_of(&sym), PostalVariant::JapanPost);
assert_eq!(sym.symbology, Symbology::JapanPost);
assert_lossless(&sym);
}
}
#[test]
fn japanpost_lowercase_is_uppercased_by_caller() {
assert!(PostalEncoder::new().build_japanpost("abc").is_err());
assert!(PostalEncoder::new().build_japanpost("12,34").is_err());
assert!(PostalEncoder::new().build_japanpost("ABCDEFGHIJK").is_err());
}
#[test]
fn mailmark_reference_barcode_c_example1() {
let sym = PostalEncoder::new()
.build_mailmark("1100000000000XY11 ")
.unwrap();
let bars = bar_letters(&sym);
assert_eq!(bars.len(), 66, "Mailmark Barcode C is 66 bars");
assert_eq!(
bars,
"TTDTTATTDTAATTDTAATTDTAATTDTTDDAATAADDATAATDDFAFTDDTAADDDTAAFDFAFF"
);
}
#[test]
fn mailmark_reference_barcode_c_example2() {
let sym = PostalEncoder::new()
.build_mailmark("21B2254800659JW5O9QA6Y")
.unwrap();
assert_eq!(
bar_letters(&sym),
"DAATATTTADTAATTFADDDDTTFTFDDDDFFDFDAFTADDTFFTDDATADTTFATTDAFDTFDDA"
);
}
#[test]
fn mailmark_reference_barcode_l_example1() {
let sym = PostalEncoder::new()
.build_mailmark("11000000000000000XY11 ")
.unwrap();
let bars = bar_letters(&sym);
assert_eq!(bars.len(), 78, "Mailmark Barcode L is 78 bars");
assert_eq!(
bars,
"TTDTTATDDTTATTDTAATTDTAATDDTTATTDTTDATFTAATDDTAATDDTATATFAADDAATAATDDTAADFTFTA"
);
}
#[test]
fn mailmark_reference_barcode_l_example2() {
let sym = PostalEncoder::new()
.build_mailmark("41038422416563762EF61AH8T")
.unwrap();
assert_eq!(
bar_letters(&sym),
"DTTFATTDDTATTTATFTDFFFTFDFDAFTTTADTTFDTFDDDTDFDDFTFAADTFDTDTDTFAATAFDDTAATTDTT"
);
}
#[test]
fn mailmark_roundtrip() {
let enc = PostalEncoder::new();
for data in [
"1100000000000XY11 ", "21B2254800659JW5O9QA6Y", "0100000000009JA500AA0A", "34A6789012345OV123JQ4U", "11000000000000000XY11 ", "41038422416563762EF61AH8T", ] {
let sym = enc.build_mailmark(data).unwrap();
assert_eq!(variant_of(&sym), PostalVariant::Mailmark);
assert_eq!(sym.symbology, Symbology::Mailmark);
assert!(matches!(bar_letters(&sym).len(), 66 | 78));
assert_lossless(&sym);
}
}
#[test]
fn mailmark_short_input_is_space_padded() {
let enc = PostalEncoder::new();
let sym = enc.build_mailmark("1100000000000XY11 ").unwrap();
assert_eq!(sym.segments[0].data.len(), 22);
assert_eq!(sym.segments[0].data, b"1100000000000XY11 ");
assert_lossless(&sym);
}
#[test]
fn mailmark_rejects_bad_fields() {
let enc = PostalEncoder::new();
assert!(enc.build_mailmark("0000000000000").is_err()); assert!(enc.build_mailmark("5100000000000XY11 ").is_err()); assert!(enc.build_mailmark("1000000000000XY11 ").is_err()); assert!(enc.build_mailmark("110000000000!XY11 ").is_err()); }
#[test]
fn distinct_codes_decode_to_their_own_symbology() {
let enc = PostalEncoder::new();
let dec = PostalDecoder::new();
let cases: [(Symbol, Symbology); 4] = [
(
enc.build_auspost("12345678", "9012345").unwrap(),
Symbology::AustraliaPost,
),
(
enc.build_japanpost("15400233-16-4-205").unwrap(),
Symbology::JapanPost,
),
(
enc.build_mailmark("41038422416563762EF61AH8T").unwrap(),
Symbology::Mailmark,
),
(enc.build_rm4scc("BX11LT1A").unwrap(), Symbology::RoyalMail),
];
for (sym, expected) in cases {
let decoded = dec.decode(&enc.encode(&sym).unwrap()).unwrap();
assert_eq!(decoded.symbology, expected);
}
}