use crate::formatting::El; use std::fs::File; use std::io::Write; use std::path::Path; use std::sync::mpsc::{RecvError, Sender, TryRecvError}; use std::thread::{sleep, JoinHandle}; use std::time::Duration; use std::{io, thread}; type ThreadWriterResult = Result<(), ThreadWriterError>; pub struct ThreadWriter { handle: JoinHandle<ThreadWriterResult>, } impl ThreadWriter { pub fn join(self) -> thread::Result<ThreadWriterResult> { self.handle.join() } } #[derive(thiserror::Error, Debug)] pub enum ThreadWriterError { #[error("There was an issue reading the incoming element: {0}")] IncomingError(#[from] RecvError), #[error("There was an issue opening the target file: {0}")] OpenFileError(io::Error), #[error("There was an issue writing to the target file: {0}")] WriteFileError(io::Error), } fn write_error(e: io::Error) -> ThreadWriterError { ThreadWriterError::WriteFileError(e) } pub fn create_output_canvas<T: AsRef<Path> + Send + 'static>( width: usize, height: usize, path: T, ) -> (Sender<String>, ThreadWriter) { let (send, receiver) = std::sync::mpsc::channel::<String>(); let handle = thread::spawn(move || { let mut file = File::create(path).map_err(write_error)?; let container_attrs = vec![ ("width", format!("{}", width)).into(), ("height", format!("{}", height)).into(), ("viewport", format!("0 0 {} {}", width, height)).into(), ]; file.write_all(El::open("svg", &container_attrs).as_bytes()) .map_err(write_error)?; file.write_all("\n".as_bytes()).map_err(write_error)?; loop { match receiver.try_recv() { Ok(value) => { file.write_all(value.as_bytes()).map_err(write_error)?; file.write_all("\n".as_bytes()).map_err(write_error)?; } Err(TryRecvError::Empty) => sleep(Duration::from_millis(5)), Err(TryRecvError::Disconnected) => break, } } file.write_all(El::close("svg").as_bytes()) .map_err(write_error)?; Ok(()) }); (send, ThreadWriter { handle }) }