use std::path::PathBuf;
use std::time::Instant;
use dqr::*;
#[derive(Debug, Clone)]
struct ResultInfo {
file_count: usize,
id_count: usize,
decode_count: usize,
load_time: u128,
identify_time: u128,
total_time: u128,
}
#[derive(Clone)]
struct Opts {
verbose: bool,
cell_dump: bool,
}
fn print_result(name: &str, info: &mut ResultInfo) {
print!("----------------------------------------------------------------------");
print!(
"{}: {:2} files, {:2} codes, {:2} decoded, ({} failures)",
name,
info.file_count,
info.id_count,
info.decode_count,
info.id_count - info.decode_count,
);
if info.id_count != 0 {
print!(", {}% success rate", (info.decode_count * 100 + info.id_count / 2) / info.id_count,);
}
println!();
println!("Total time: load: {} identify: {} total: {}", info.load_time, info.identify_time, info.total_time,);
if info.file_count != 0 {
println!(
"Average time: load: {} identify: {} total: {}",
info.load_time.wrapping_div(info.file_count as u128),
info.identify_time.wrapping_div(info.file_count as u128),
info.total_time.wrapping_div(info.file_count as u128),
);
}
}
fn add_result(sum: &mut ResultInfo, inf: &mut ResultInfo) {
sum.file_count += inf.file_count;
sum.id_count += inf.id_count;
sum.decode_count += inf.decode_count;
sum.load_time = sum.load_time.wrapping_add(inf.load_time);
sum.identify_time = sum.identify_time.wrapping_add(inf.identify_time);
sum.total_time = sum.total_time.wrapping_add(inf.total_time);
}
fn scan_file(decoder: &mut Quirc, opts: &Opts, path: &str, info: &mut ResultInfo) -> i32 {
let path = PathBuf::from(path);
let start = Instant::now();
let total_start = start;
let img = match image::open(&path) {
Ok(im) => im.into_luma8(),
Err(e) => {
println!("# {}: {e}, no image", path.display());
return 42;
}
};
info.load_time = start.elapsed().as_millis();
let start = Instant::now();
let res: Vec<_> = decoder.identify(img.width() as usize, img.height() as usize, &img).collect::<Result<_, _>>().unwrap();
info.identify_time = start.elapsed().as_millis();
info.id_count = decoder.count();
for code in &res {
if code.decode().is_ok() {
info.decode_count += 1
}
}
info.total_time = total_start.elapsed().as_millis();
if opts.verbose {
println!("= {} (found: {} decoded: {})", path.file_name().unwrap().to_string_lossy(), info.id_count, info.decode_count,);
};
for code in &res {
if opts.cell_dump {
dump_cells(code);
println!();
};
match code.decode() {
Ok(data) => {
if opts.verbose {
println!(
"- Version: {} ECC: {:?} Mask: {} Type: {:?} Len: {} ECI: {:?} Data:",
data.version,
data.ecc_level,
data.mask,
data.data_type.unwrap(),
data.payload.len(),
data.eci
);
};
println!("{}", std::str::from_utf8(&data.payload).unwrap());
}
Err(err) => {
println!("# ERROR: {err}");
}
};
}
info.file_count = 1;
1
}
fn run_tests(opts: &Opts, args: &[String]) -> i32 {
let mut sum = ResultInfo { file_count: 0, id_count: 0, decode_count: 0, load_time: 0, identify_time: 0, total_time: 0 };
let mut count: i32 = 0;
let mut decoder = Quirc::new();
for path in args {
let mut info: ResultInfo = ResultInfo { file_count: 0, id_count: 0, decode_count: 0, load_time: 0, identify_time: 0, total_time: 0 };
if scan_file(&mut decoder, opts, path, &mut info) > 0 {
add_result(&mut sum, &mut info);
count += 1
}
}
if count > 1 && opts.verbose {
print_result("\nTOTAL", &mut sum);
}
0
}
fn dump_cells(code: &Code) {
let code = *code;
print!("- {} cells, corners:", code.size);
for u in 0..4 {
print!(" ({},{})", code.corners[u].x, code.corners[u].y);
}
println!();
for _ in 0..code.size + 2 {
print!("██");
}
println!();
for v in 0..code.size {
print!("██");
for u in 0..code.size {
let p = v * code.size + u;
if (code.cell_bitmap[(p >> 3) as usize] & (1 << (p & 7))) != 0 {
print!(" ");
} else {
print!("██");
}
}
println!("██");
}
for _ in 0..code.size + 2 {
print!("██");
}
}
fn help() {
println!("{} {} - Decode QR with Quircs on CLI", env!("CARGO_PKG_NAME"), version());
println!("Usage: {} [-h|--help] | [-v|--verbose] [-d|--dump] <image>...", env!("CARGO_PKG_NAME"));
println!(" -h/--help Show this help text");
println!(" -v/--verbose how additional information on each QR");
println!(" -d/--dump Dump each identified QR code to the terminal");
std::process::exit(0);
}
fn main() {
let mut opts = Opts { verbose: false, cell_dump: false };
let args: Vec<String> = std::env::args()
.skip(1)
.filter(|e| {
if e == "-v" || e == "--verbose" {
opts.verbose = true;
false
} else {
true
}
})
.filter(|e| {
if e == "-d" || e == "--dump" {
opts.cell_dump = true;
false
} else {
true
}
})
.filter(|e| {
if e == "-h" || e == "--help" {
help();
false
} else {
true
}
})
.collect();
if opts.verbose {
println!("{} {} - Decode QR with Quircs on CLI", env!("CARGO_PKG_NAME"), version());
};
if args.is_empty() {
help();
}
let res = run_tests(&opts, &args);
std::process::exit(res);
}