use std::collections::VecDeque;
use std::fmt::{Debug, Display, Formatter};
use std::io::Write;
use std::process::{Command, Stdio, Child, ChildStdin};
use crate::prelude::*;
pub struct Render;
pub enum RenderError {
CommandGenerationFailed(String),
WritingCommandFailed,
GnuSpawnFailed,
GnuSTDINNotAccessible,
WaitingForGnuPlotFailed
}
impl Display for RenderError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let msg = match self {
RenderError::CommandGenerationFailed(msg) => format!("Command generation failed: {}", msg),
RenderError::WritingCommandFailed => "Failed to `write` command to GnuPlot".into(),
RenderError::GnuSpawnFailed => "Failed to spawn GnuPlot. Verify it is correctly installed and available".into(),
RenderError::GnuSTDINNotAccessible => "GnuPlot STDIN cannot be accessed".into(),
RenderError::WaitingForGnuPlotFailed => "Waiting for Gnu Plot failed. Check your command syntax for errors.".into()
};
f.write_fmt(format_args!("{}", msg))
}
}
impl Debug for RenderError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{}", self))
}
}
impl From<GnuCommandFactoryError> for RenderError {
fn from(value: GnuCommandFactoryError) -> Self {
match value {
GnuCommandFactoryError::Message(msg) =>
RenderError::CommandGenerationFailed(msg),
GnuCommandFactoryError::RequiredValueMissing(msg) =>
RenderError::CommandGenerationFailed(msg),
GnuCommandFactoryError::IOError(msg) =>
RenderError::CommandGenerationFailed(msg),
}
}
}
pub type RenderResult<T> = std::result::Result<T, RenderError>;
impl Render {
pub fn render(commands: VecDeque<GnuCommand>) -> RenderResult<()> {
let commands: Vec<GnuCommand> = commands.into();
let mut gnu = Command::new("gnuplot")
.arg("-p")
.stdin(Stdio::piped())
.spawn()
.map_err(|_| RenderError::GnuSpawnFailed)?;
let stdin = gnu.stdin.as_mut().ok_or(RenderError::GnuSTDINNotAccessible)?;
writeln!(stdin, "clear").or(Err(RenderError::WritingCommandFailed))?;
for command in commands {
writeln!(stdin, "{}", command).or(Err(RenderError::WritingCommandFailed))?;
}
writeln!(stdin, "exit").or(Err(RenderError::WritingCommandFailed))?;
gnu.wait().or(Err(RenderError::WaitingForGnuPlotFailed))?;
Ok(())
}
}
pub trait CanRender: GnuCommandFactory {
fn render(&self) -> RenderResult<()> {
let commands = self.as_commands()?;
Render::render(commands)
}
}