Skip to content
Snippets Groups Projects
output.rs 1.92 KiB
Newer Older
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 })
}