covet 0.5.5

A CLI and web frontend for scanning with HP Envy printer / scanners
extern crate base64;
#[macro_use]
extern crate clap;
extern crate hyper;
extern crate hyper_native_tls;
extern crate iron;
extern crate router;
extern crate sha2;
extern crate time;
extern crate urlencoded;
extern crate xml;
extern crate xmltree;

mod cli;
mod scanner;
mod message;
mod web;

use std::thread;
use std::time::Duration;

use message::scan_job::{ScanJob, InputSource, Format, ColorSpace};
use message::scan_status::AdfState;
use scanner::{Scanner, ScannerError};

fn main() {
    let matches = cli::build_cli().get_matches();
    if let Some(matches) = matches.subcommand_matches("status") {
        let host = matches.value_of("SCANNER").unwrap();
        let use_tls = !matches.is_present("no-tls");
        status(host, use_tls);
    } else if let Some(matches) = matches.subcommand_matches("scan") {
        let host = matches.value_of("SCANNER").unwrap();
        let use_tls = !matches.is_present("no-tls");
        let format = value_t!(matches.value_of("FORMAT"), cli::Format).unwrap();
        let color = value_t!(matches.value_of("COLORSPACE"), cli::ColorSpace).unwrap();
        let source = value_t!(matches.value_of("SOURCE"), cli::Source).unwrap();
        let resolution = value_t!(matches.value_of("RESOLUTION"), u32).unwrap();
        scan(
            host,
            use_tls,
            format.to_internal(),
            color.to_internal(),
            &source,
            resolution,
        ).unwrap_or_else(|e| println!("Error: {}", e));
    } else if let Some(matches) = matches.subcommand_matches("web") {
        let host = matches.value_of("SCANNER").unwrap();
        let use_tls = !matches.is_present("no-tls");
        let addr = matches.value_of("ADDR").unwrap();
        let port = value_t!(matches, "PORT", u16).unwrap();
        web::run_server(host, addr, port, use_tls);
    }
}

fn status(host: &str, use_tls: bool) {
    let scanner = Scanner::new(host, use_tls);
    print_scan_status(&scanner).unwrap_or_else(|e| println!("Error: {}", e));
}

fn print_scan_status(scanner: &Scanner) -> Result<(), ScannerError> {
    println!("Status of scanner {}", scanner.host());
    let status = scanner.get_scan_status()?;
    println!(
        "Scanner: {:?}, Adf: {:?}",
        status.scanner_state(),
        status.adf_state()
    );
    Ok(())
}

impl cli::Format {
    fn to_internal(&self) -> Format {
        match *self {
            cli::Format::pdf => Format::Pdf,
            cli::Format::jpeg => Format::Jpeg,
        }
    }
}

impl cli::ColorSpace {
    fn to_internal(&self) -> ColorSpace {
        match *self {
            cli::ColorSpace::gray => ColorSpace::Gray,
            cli::ColorSpace::color => ColorSpace::Color,
        }
    }
}

fn choose_source(source: &cli::Source, adf_state: AdfState) -> Result<InputSource, ScannerError> {
    let input_source = match *source {
        cli::Source::auto => {
            if adf_state == AdfState::Loaded {
                InputSource::Adf
            } else {
                InputSource::Platen
            }
        }
        cli::Source::adf => {
            if adf_state == AdfState::Loaded {
                InputSource::Adf
            } else {
                return Err(ScannerError::AdfEmpty);
            }
        }
        cli::Source::glass => InputSource::Platen,
    };
    Ok(input_source)
}

fn scan(
    host: &str,
    use_tls: bool,
    format: Format,
    color: ColorSpace,
    source: &cli::Source,
    resolution: u32,
) -> Result<(), ScannerError> {
    let scanner = Scanner::new(host, use_tls);
    let status = scanner.get_scan_status()?;
    if !status.is_idle() {
        return Err(ScannerError::Busy);
    }
    let input_source = choose_source(source, status.adf_state())?;
    let mut job = scanner.start_job(
        ScanJob::new(input_source, resolution, format, color),
    )?;
    println!("Job: {:?}", job);
    loop {
        let ready = job.retrieve_status()?;
        if ready {
            println!("Job: {:?}", job);
            let output_file = scanner::output_file_name(&format, &time::now());
            job.download_to_file(&output_file)?;
            break;
        }
        thread::sleep(Duration::from_millis(500));
    }
    Ok(())
}