rxing-cli 0.1.2

A command line interface for rxing supporting encoding and decoding of multiple barcode formats
use std::{
    collections::{HashMap, HashSet},
    path::PathBuf,
};

use clap::{Parser, Subcommand};
use rxing::{BarcodeFormat, MultiFormatWriter, Writer};

#[derive(Parser)]
struct Args {
    file_name: String,
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    Decode {
        #[arg(short, long)]
        try_harder: bool,
        #[arg(short, long)]
        decode_multi: bool,
        #[arg(short, long, value_enum)]
        barcode_types: Option<Vec<BarcodeFormat>>,
    },
    Encode {
        barcode_type: BarcodeFormat,
        #[arg(long)]
        width: u32,
        #[arg(long)]
        height: u32,
        #[arg(short, long)]
        data: Option<String>,
        #[arg(long)]
        data_file: Option<String>,
    },
}

fn main() {
    println!("rxing-cli");
    let cli = Args::parse();
    match &cli.command {
        Commands::Decode {
            try_harder,
            decode_multi,
            barcode_types,
        } => decode_command(&cli.file_name, try_harder, decode_multi, barcode_types),
        Commands::Encode {
            barcode_type,
            width,
            height,
            data,
            data_file,
        } => encode_command(&cli.file_name, barcode_type, width, height, data, data_file),
    }
}

fn decode_command(
    file_name: &str,
    try_harder: &bool,
    decode_multi: &bool,
    barcode_types: &Option<Vec<BarcodeFormat>>,
) {
    println!(
        "Decode '{}' with: try_harder: {}, decode_multi: {}, barcode_types: {:?}",
        file_name, try_harder, decode_multi, barcode_types
    );
    let mut hints: rxing::DecodingHintDictionary = HashMap::new();
    if !try_harder {
        hints.insert(
            rxing::DecodeHintType::TRY_HARDER,
            rxing::DecodeHintValue::TryHarder(false),
        );
    }
    if let Some(barcode_type) = barcode_types {
        hints.insert(
            rxing::DecodeHintType::POSSIBLE_FORMATS,
            rxing::DecodeHintValue::PossibleFormats(HashSet::from_iter(
                barcode_type.iter().copied(),
            )),
        );
    }

    if *decode_multi {
        let results = rxing::helpers::detect_multiple_in_file_with_hints(file_name, &mut hints);
        match results {
            Ok(result_array) => {
                println!("Found {} results", result_array.len());
                for (i, result) in result_array.into_iter().enumerate() {
                    println!("Result {}: ({}) {}", i, result.getBarcodeFormat(), result);
                }
            }
            Err(search_err) => {
                println!(
                    "Error while attempting to locate multiple barcodes in '{}': {}",
                    file_name, search_err
                );
            }
        }
    } else {
        let result = rxing::helpers::detect_in_file_with_hints(file_name, None, &mut hints);
        match result {
            Ok(result) => {
                println!(
                    "Detection result: \n({}) {}",
                    result.getBarcodeFormat(),
                    result
                );
            }
            Err(search_err) => {
                println!(
                    "Error while attempting to locate barcode in '{}': {}",
                    file_name, search_err
                );
            }
        }
    }
}

fn encode_command(
    file_name: &str,
    barcode_type: &BarcodeFormat,
    width: &u32,
    height: &u32,
    data: &Option<String>,
    data_file: &Option<String>,
) {
    if data.is_none() && data_file.is_none() {
        println!("must provide either data string or data file");
        return;
    }
    if data.is_some() && data_file.is_some() {
        println!("provide only data string or data file");
        return;
    }

    let input_data = if let Some(df) = data_file {
        let path_from = PathBuf::from(df);
        if path_from.exists() {
            let Ok(fl) = std::fs::File::open(path_from) else {
                println!("file cannot be opened");
                return;
            };
            std::io::read_to_string(fl).expect("file should read")
        } else {
            println!("{} does not exist", df);
            return;
        }
    } else if let Some(ds) = data {
        ds.to_owned()
    } else {
        println!("Unknown error getting data");
        return;
    };

    println!("Encode: file_name: {}, barcode_type: {}, width: {:?}, height: {:?}, data: '{:?}', data_file: {:?}", file_name, barcode_type, width, height, data, data_file);
    let writer = MultiFormatWriter::default();
    match writer.encode_with_hints(
        &input_data,
        barcode_type,
        *width as i32,
        *height as i32,
        &HashMap::new(),
    ) {
        Ok(result) => {
            println!("Encode successful, saving...");
            match rxing::helpers::save_image(file_name, &result) {
                Ok(_) => println!("Saved to '{}'", file_name),
                Err(error) => println!("Could not save '{}': {}", file_name, error),
            }
        }
        Err(encode_error) => println!("Couldn't encode: {}", encode_error),
    }
}