📄 src/receipt_printer/receipt_printer_actor.rs
use std::sync::Arc;

use escpos::errors::PrinterError;
use tokio::sync::{mpsc, oneshot};

use super::escpos_printer::EscposPrinter;

pub struct EscposPrinterActor {
    host: String,
    port: u16,
    printer: Option<EscposPrinter>,
    receiver: mpsc::UnboundedReceiver<EscposPrinterRequest>,
}

pub enum EscposPrinterRequest {
    PrintImage(Arc<[u8]>, oneshot::Sender<Result<(), PrinterError>>),
}

impl EscposPrinterActor {
    pub fn run(host: String, port: u16, receiver: mpsc::UnboundedReceiver<EscposPrinterRequest>) {
        let mut actor = Self {
            host,
            port,
            printer: None,
            receiver,
        };

        tokio::spawn(async move {
            while let Some(request) = actor.receiver.recv().await {
                let response_result = match request {
                    EscposPrinterRequest::PrintImage(image, responder) => {
                        responder.send(actor.print_image(&image))
                    }
                };
                if let Err(error) = response_result {
                    println!("Could not respond to request: {error:?}");
                }
            }
        });
    }

    pub fn print_image(&mut self, image: &[u8]) -> Result<(), PrinterError> {
        let printer = self.ensure_printer_connection()?;

        printer.print_image(image)
    }

    fn ensure_printer_connection<'s>(&'s mut self) -> Result<&'s mut EscposPrinter, PrinterError> {
        let printer = match self.printer.take() {
            Some(printer) => printer,
            None => EscposPrinter::open(&self.host, self.port)?,
        };
        Ok(self.printer.insert(printer))
    }
}