use crate::{Result, SubplotError};
use std::env;
use std::ffi::OsString;
use std::io::prelude::*;
use std::path::PathBuf;
use std::process::{Command, Stdio};
pub trait GraphMarkup {
fn as_svg(&self) -> Result<Vec<u8>>;
}
pub struct PikchrMarkup {
markup: String,
class: Option<String>,
}
impl PikchrMarkup {
pub fn new(markup: &str, class: Option<&str>) -> PikchrMarkup {
PikchrMarkup {
markup: markup.to_owned(),
class: class.map(str::to_owned),
}
}
}
impl GraphMarkup for PikchrMarkup {
fn as_svg(&self) -> Result<Vec<u8>> {
let mut flags = pikchr::PikchrFlags::default();
flags.generate_plain_errors();
let image = pikchr::Pikchr::render(&self.markup, self.class.as_deref(), flags)
.map_err(SubplotError::PikchrRenderError)?;
Ok(image.as_bytes().to_vec())
}
}
pub struct DotMarkup {
markup: String,
}
impl DotMarkup {
pub fn new(markup: &str) -> DotMarkup {
DotMarkup {
markup: markup.to_owned(),
}
}
}
impl GraphMarkup for DotMarkup {
fn as_svg(&self) -> Result<Vec<u8>> {
let mut child = Command::new("dot")
.arg("-Tsvg")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
if let Some(stdin) = child.stdin.as_mut() {
stdin.write_all(self.markup.as_bytes())?;
let output = child.wait_with_output()?;
if output.status.success() {
Ok(output.stdout)
} else {
Err(SubplotError::child_failed("dot", &output))
}
} else {
Err(SubplotError::ChildNoStdin)
}
}
}
pub struct PlantumlMarkup {
markup: String,
}
impl PlantumlMarkup {
pub fn new(markup: &str) -> PlantumlMarkup {
PlantumlMarkup {
markup: markup.to_owned(),
}
}
fn build_java_path() -> Option<OsString> {
let java_home = env::var_os("JAVA_HOME")?;
let cur_path = env::var_os("PATH")?;
let cur_path: Vec<_> = env::split_paths(&cur_path).collect();
let java_home = PathBuf::from(java_home);
let java_bin = java_home.join("bin");
if cur_path.iter().any(|v| v.as_os_str() == java_bin) {
return None;
}
env::join_paths(Some(java_bin).iter().chain(cur_path.iter())).ok()
}
}
impl GraphMarkup for PlantumlMarkup {
fn as_svg(&self) -> Result<Vec<u8>> {
let mut cmd = Command::new("java");
cmd.arg("-Djava.awt.headless=true")
.arg("-jar")
.arg("/usr/share/plantuml/plantuml.jar")
.arg("--")
.arg("-pipe")
.arg("-tsvg")
.arg("-v")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped());
if let Some(path) = Self::build_java_path() {
cmd.env("PATH", path);
}
let mut child = cmd.spawn()?;
if let Some(stdin) = child.stdin.as_mut() {
stdin.write_all(self.markup.as_bytes())?;
let output = child.wait_with_output()?;
if output.status.success() {
Ok(output.stdout)
} else {
Err(SubplotError::child_failed("plantuml", &output))
}
} else {
Err(SubplotError::ChildNoStdin)
}
}
}