printers 2.3.0

Get printers and print files on unix and windows
Documentation
use cups::dests::get_dests;
use std::str;

use crate::common::{
    base::{
        errors::PrintersError,
        job::{PrinterJob, PrinterJobOptions, PrinterJobState},
        printer::{Printer, PrinterState},
    },
    converters::Converter,
    traits::platform::{PlatformActions, PlatformPrinterGetters},
    utils::file,
};

mod cups;
mod utils;

impl PlatformActions for crate::Platform {
    fn get_printers() -> Vec<Printer> {
        if let Some(dests) = get_dests() {
            let printers = dests
                .iter()
                .map(|p| Printer::from_platform_printer_getters(p))
                .collect();

            cups::dests::free(dests);
            printers
        } else {
            Vec::new()
        }
    }

    fn print(
        printer_system_name: &str,
        buffer: &[u8],
        options: PrinterJobOptions,
    ) -> Result<u64, PrintersError> {
        let buffer = options.converter.convert(buffer)?;
        let buffer = &buffer.as_slice();
        let file_path = file::save_tmp_file(buffer)?;

        cups::jobs::print_file(
            printer_system_name,
            file_path.to_str().unwrap_or_default(),
            options.name,
            options.raw_properties,
        )
    }

    fn print_file(
        printer_system_name: &str,
        file_path: &str,
        options: PrinterJobOptions,
    ) -> Result<u64, PrintersError> {
        if options.converter != Converter::None {
            let buffer = file::get_file_as_bytes(file_path)?;
            return Self::print(printer_system_name, buffer.as_slice(), options);
        }

        cups::jobs::print_file(
            printer_system_name,
            file_path,
            options.name,
            options.raw_properties,
        )
    }

    fn get_printer_jobs(printer_name: &str, active_only: bool) -> Vec<PrinterJob> {
        cups::jobs::get_printer_jobs(printer_name, active_only)
            .unwrap_or_default()
            .iter()
            .map(|j| PrinterJob::from_platform_printer_job_getters(j))
            .collect()
    }

    fn get_default_printer() -> Option<Printer> {
        let dests = get_dests()?;
        let printer = dests
            .iter()
            .find(|d| d.get_is_default())
            .map(|d| Printer::from_platform_printer_getters(d));

        cups::dests::free(dests);
        printer
    }

    fn get_printer_by_name(printer_name: &str) -> Option<Printer> {
        let dests = get_dests()?;
        let dest = dests
            .iter()
            .find(|d| d.get_name() == printer_name || d.get_system_name() == printer_name)
            .map(|d| Printer::from_platform_printer_getters(d));

        cups::dests::free(dests);
        dest
    }

    fn parse_printer_state(platform_state: u64, state_reasons: &str) -> PrinterState {
        if state_reasons.contains("offline-report") {
            return PrinterState::OFFLINE;
        }

        match platform_state {
            3 => PrinterState::READY,
            4 => PrinterState::PRINTING,
            5 => PrinterState::PAUSED,
            _ => PrinterState::UNKNOWN,
        }
    }

    fn parse_printer_job_state(platform_state: u64) -> PrinterJobState {
        match platform_state {
            3 => PrinterJobState::PENDING,
            4 | 6 => PrinterJobState::PAUSED,
            5 => PrinterJobState::PROCESSING,
            7 | 8 => PrinterJobState::CANCELLED,
            9 => PrinterJobState::COMPLETED,
            _ => PrinterJobState::UNKNOWN,
        }
    }

    fn set_job_state(
        printer_name: &str,
        job_id: u64,
        state: PrinterJobState,
    ) -> Result<(), PrintersError> {
        let result = match state {
            PrinterJobState::PENDING => cups::jobs::restart_job(printer_name, job_id as i32),
            PrinterJobState::PROCESSING => cups::jobs::release_job(printer_name, job_id as i32),
            PrinterJobState::PAUSED => cups::jobs::hold_job(printer_name, job_id as i32),
            PrinterJobState::CANCELLED => cups::jobs::cancel_job(printer_name, job_id as i32),
            _ => false,
        };

        if result {
            Ok(())
        } else {
            Err(PrintersError::print_error("cups method failed"))
        }
    }
}