use anyd::Symbology;
use anyd::codes::ean::{AddOnKind, EanDecoder, EanEncoder, EanVariant};
use anyd::output::Encoding;
use anyd::segment::Segment;
use anyd::symbol::SymbolMeta;
use anyd::traits::{Decode, Encode};
fn digits_of(symbol: &anyd::Symbol) -> String {
let seg = symbol
.segments
.iter()
.find(|s| matches!(s.mode, anyd::Mode::Numeric))
.expect("numeric segment");
String::from_utf8(seg.data.clone()).unwrap()
}
fn ean_meta(symbol: &anyd::Symbol) -> (EanVariant, Option<(AddOnKind, String)>) {
match &symbol.meta {
SymbolMeta::Ean(m) => (
m.variant,
m.addon
.as_ref()
.map(|a| (a.kind, String::from_utf8(a.digits.clone()).unwrap())),
),
_ => panic!("not an EAN symbol"),
}
}
fn assert_lossless(symbol: &anyd::Symbol) {
let enc = EanEncoder::new();
let encoding = enc.encode(symbol).unwrap();
let decoded = EanDecoder::new().decode(&encoding).unwrap();
assert_eq!(decoded.segments, symbol.segments, "segments differ");
assert_eq!(decoded.meta, symbol.meta, "meta differs");
let reencoded = enc.encode(&decoded).unwrap();
assert_eq!(reencoded, encoding, "re-encoding not identical");
}
fn module_len(symbol: &anyd::Symbol) -> usize {
match EanEncoder::new().encode(symbol).unwrap() {
Encoding::Linear(lp) => lp.modules.len(),
Encoding::Matrix(_) => panic!("expected linear"),
}
}
#[test]
fn ean13_reference_check_digit() {
let sym = EanEncoder::new().build_ean13("590123412345").unwrap();
assert_eq!(digits_of(&sym), "5901234123457");
assert_eq!(module_len(&sym), 95);
}
#[test]
fn upca_reference_check_digit() {
let sym = EanEncoder::new().build_upca("03600029145").unwrap();
assert_eq!(digits_of(&sym), "036000291452");
assert_eq!(module_len(&sym), 95);
}
#[test]
fn upce_reference_pair() {
let sym = EanEncoder::new().build_upce("0425261").unwrap();
assert_eq!(digits_of(&sym), "04252614");
assert_eq!(module_len(&sym), 51);
let sym8 = EanEncoder::new().build_upce("04252614").unwrap();
assert_eq!(digits_of(&sym8), "04252614");
assert!(EanEncoder::new().build_upce("04252619").is_err());
}
#[test]
fn ean5_reference_checksum() {
let sym = EanEncoder::new().build_ean5("52495").unwrap();
assert_eq!(digits_of(&sym), "52495");
assert_eq!(module_len(&sym), 47);
}
#[test]
fn ean8_reference_check_digit() {
let sym = EanEncoder::new().build_ean8("9638507").unwrap();
assert_eq!(digits_of(&sym), "96385074");
assert_eq!(module_len(&sym), 67);
}
#[test]
fn roundtrip_ean13() {
let sym = EanEncoder::new().build_ean13("590123412345").unwrap();
assert_eq!(ean_meta(&sym).0, EanVariant::Ean13);
assert_eq!(sym.symbology, Symbology::Ean13);
assert_lossless(&sym);
}
#[test]
fn roundtrip_ean8() {
let sym = EanEncoder::new().build_ean8("9638507").unwrap();
assert_eq!(ean_meta(&sym).0, EanVariant::Ean8);
assert_lossless(&sym);
}
#[test]
fn roundtrip_upca() {
let sym = EanEncoder::new().build_upca("03600029145").unwrap();
assert_eq!(ean_meta(&sym).0, EanVariant::UpcA);
assert_lossless(&sym);
}
#[test]
fn roundtrip_upce_both_number_systems() {
for code in ["04252614", "0425261", "1123456"] {
let sym = EanEncoder::new().build_upce(code).unwrap();
assert_eq!(ean_meta(&sym).0, EanVariant::UpcE);
assert_eq!(sym.symbology, Symbology::UpcE);
assert_lossless(&sym);
}
}
#[test]
fn roundtrip_ean2_standalone() {
for code in ["00", "01", "02", "03", "37", "99"] {
let sym = EanEncoder::new().build_ean2(code).unwrap();
assert_eq!(ean_meta(&sym).0, EanVariant::Ean2);
assert_eq!(module_len(&sym), 20);
assert_lossless(&sym);
}
}
#[test]
fn roundtrip_ean5_standalone() {
for code in ["52495", "00000", "12345", "99999"] {
let sym = EanEncoder::new().build_ean5(code).unwrap();
assert_eq!(ean_meta(&sym).0, EanVariant::Ean5);
assert_lossless(&sym);
}
}
#[test]
fn roundtrip_ean13_with_ean5_addon() {
let enc = EanEncoder::new();
let base = enc.build_ean13("978030640615").unwrap(); let sym = enc.with_addon(base, "52495").unwrap();
let (variant, addon) = ean_meta(&sym);
assert_eq!(variant, EanVariant::Ean13);
assert_eq!(addon, Some((AddOnKind::Five, "52495".to_string())));
assert_eq!(module_len(&sym), 95 + 9 + 47);
assert_lossless(&sym);
}
#[test]
fn roundtrip_ean13_with_ean2_addon() {
let enc = EanEncoder::new();
let base = enc.build_ean13("590123412345").unwrap();
let sym = enc.with_addon(base, "05").unwrap();
assert_eq!(ean_meta(&sym).1, Some((AddOnKind::Two, "05".to_string())));
assert_lossless(&sym);
}
#[test]
fn roundtrip_upca_with_ean2_addon() {
let enc = EanEncoder::new();
let base = enc.build_upca("03600029145").unwrap();
let sym = enc.with_addon(base, "12").unwrap();
assert_lossless(&sym);
}
#[test]
fn roundtrip_upce_with_ean5_addon() {
let enc = EanEncoder::new();
let base = enc.build_upce("04252614").unwrap();
let sym = enc.with_addon(base, "12345").unwrap();
assert_lossless(&sym);
}
#[test]
fn roundtrip_ean8_with_ean5_addon() {
let enc = EanEncoder::new();
let base = enc.build_ean8("9638507").unwrap();
let sym = enc.with_addon(base, "54321").unwrap();
assert_lossless(&sym);
}
#[test]
fn leading_zero_ean13_decodes_as_upca() {
let enc = EanEncoder::new();
let sym = enc.build_ean13("0123456789012").unwrap();
let encoding = enc.encode(&sym).unwrap();
let decoded = EanDecoder::new().decode(&encoding).unwrap();
assert_eq!(decoded.symbology, Symbology::UpcA);
assert_eq!(digits_of(&decoded), "123456789012");
assert_eq!(enc.encode(&decoded).unwrap(), encoding);
}
#[test]
fn upca_and_leading_zero_ean13_share_pattern() {
let enc = EanEncoder::new();
let ean = enc.build_ean13("0036000291452").unwrap();
let upca = enc.build_upca("036000291452").unwrap();
assert_eq!(enc.encode(&ean).unwrap(), enc.encode(&upca).unwrap());
}
#[test]
fn rejects_bad_check_digit() {
assert!(EanEncoder::new().build_ean13("5901234123458").is_err());
}
#[test]
fn rejects_non_digits() {
assert!(EanEncoder::new().build_ean13("59012341234A").is_err());
assert!(EanEncoder::new().build_ean8("ABCDEFG").is_err());
}
#[test]
fn manual_symbol_roundtrips() {
use anyd::Symbol;
use anyd::codes::ean::EanMeta;
let sym = Symbol::new(
Symbology::Ean13,
vec![Segment::numeric(b"4006381333931".to_vec())],
SymbolMeta::Ean(EanMeta::new(EanVariant::Ean13)),
);
assert_lossless(&sym);
}