use crate::{error, Result};
use askama::Template;
use log::debug;
use ommui_data::filesystem::DeviceBuilder;
use ommui_data::LoadableDevice;
use ommui_file_loading::PathLoad;
use snafu::ResultExt;
use std::io::Write;
use std::path::PathBuf;
use structopt::StructOpt;
use xio_instructionset::InstructionMap;
#[derive(StructOpt, Debug)]
pub struct Arguments {
#[structopt(parse(from_os_str), default_value = ".")]
pub path: PathBuf,
#[structopt(parse(from_os_str), default_value = "doc")]
pub output: PathBuf,
#[structopt(
long = "instructionset",
parse(from_os_str),
default_value = "/usr/share/xio-schema-definition/instructionset/1"
)]
pub instructionset: PathBuf,
#[structopt(parse(from_os_str), long = "asciidoctor-stylesdir")]
pub asciidoctor_stylesdir: Option<PathBuf>,
#[structopt(parse(from_os_str), long = "asciidoctor-stylesheet")]
pub asciidoctor_stylesheet: Option<PathBuf>,
}
impl Arguments {
pub fn run(&self, product_id: &str, company_id: &str) -> Result<()> {
debug!("Building device from path {:?}", &self.path);
debug!(
"product_id: {:?}, company_id: {:?}",
product_id, company_id
);
let device = DeviceBuilder::new(product_id, company_id)
.base_system_path(&self.path)
.build()
.context(error::OmmuiData)?;
debug!("Loading device");
let description =
device.load_device().context(error::OmmuiData)?;
println!("Writing documentation to {:?}", self.output);
debug!("Creating directory {:?}", &self.output);
std::fs::create_dir_all(&self.output).context(error::Io)?;
let device_description = &description.description.samplic;
let icon_id = &device_description.icon_id;
let icon_source = device.samplic().icon_file(icon_id);
let icon_filename = icon_source.file_name().unwrap();
let icon_dest = self.output.join(icon_filename);
debug!("Copying {:?} to {:?}", icon_source, icon_dest);
std::fs::copy(&icon_source, icon_dest).context(error::Io)?;
{
for (ref sampling_id, ref sampling) in
description.samplings.iter()
{
let sampling_dir = self
.output
.join("sampling")
.join(sampling_id)
.join("arnalisa");
debug!("Creating directory {:?}", sampling_dir);
std::fs::create_dir_all(&sampling_dir)
.context(error::Io)?;
for (ref bin_id, ref bin) in sampling.arnalisa.bins.iter()
{
let dot_file_name =
sampling_dir.join(format!("{}.dot", bin_id));
debug!(
"Writing bin {:?} to {:?}",
bin_id, dot_file_name
);
{
let mut dot_file =
std::fs::File::create(&dot_file_name)
.context(error::Io)?;
use arnalisa::bins::WriteDot;
bin.write_dot(&mut dot_file, &bin_id)
.context(error::Arnalisa)?;
}
let mut cmd = std::process::Command::new("dot");
cmd.arg("-Tsvg").arg(&dot_file_name);
debug!("Calling {:?}", cmd);
let output =
cmd.output().expect("Couldn't run dot command");
let svg_file_name =
sampling_dir.join(format!("{}.svg", bin_id));
debug!("Writing svg file {:?}", svg_file_name);
let mut svg_file =
std::fs::File::create(&svg_file_name)
.context(error::Io)?;
svg_file
.write_all(&output.stdout)
.context(error::Io)?;
}
}
}
{
let instructions = InstructionMap::load_from_path(
&self.instructionset.join("instructionset.json"),
)
.context(error::OmmuiFileLoading {})?;
for (ref sampling_id, ref sampling) in
description.samplings.iter()
{
let sampling_dir = self
.output
.join("sampling")
.join(sampling_id)
.join("xio");
debug!("Creating directory {:?}", sampling_dir);
std::fs::create_dir_all(&sampling_dir)
.context(error::Io)?;
for (ref job_id, ref job) in sampling.xio.jobs.iter() {
let blockdiag_file_name =
sampling_dir.join(format!("{}.blockdiag", job_id));
debug!(
"Writing job {:?} to {:?}",
job_id, blockdiag_file_name
);
{
let mut blockdiag_file =
std::fs::File::create(&blockdiag_file_name)
.context(error::Io)?;
use ommui_data::xio_job_to_blockdiag::WriteJob;
blockdiag_file
.write_job(&job, &instructions)
.context(error::OmmuiData)?;
}
let font =
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf";
let svg_file_name =
sampling_dir.join(format!("{}.svg", job_id));
let mut cmd = std::process::Command::new("blockdiag3");
cmd.arg("-Tsvg")
.arg("--font")
.arg(font)
.arg("-o")
.arg(&svg_file_name)
.arg(&blockdiag_file_name);
debug!("Running {:?}", cmd);
cmd.output().expect("Couldn't run blockdiag3 command");
{
debug!("Fixing SVG file {:?}", svg_file_name);
debug!("(adding width and height attributes)");
let data = std::fs::read_to_string(&svg_file_name)
.context(error::Io)?;
let mut element: minidom::Element =
data.parse().unwrap();
let viewbox = element.attr("viewBox").unwrap();
let elements: Vec<String> = viewbox
.split(' ')
.map(|s| s.to_string())
.collect();
element.set_attr("width", &elements[2]);
element.set_attr("height", &elements[3]);
debug!("Writing fixed file {:?}", svg_file_name);
let mut writer =
quick_xml::Writer::new_with_indent(
std::fs::File::create(svg_file_name)
.context(error::Io)?,
b' ',
4,
);
element.to_writer(&mut writer).unwrap();
}
}
}
}
let index = Index {
device: description,
company_name: company_id.to_string(),
};
let asciidoc_file = self.output.join("index.asciidoc");
debug!("Writing asciidoc file {:?}", asciidoc_file);
{
let mut f = std::fs::File::create(&asciidoc_file)
.context(error::Io)?;
f.write_all(&index.render().unwrap().as_bytes())
.context(error::Io)?;
}
let mut cmd = std::process::Command::new("asciidoctor");
if let Some(ref stylesdir) = self.asciidoctor_stylesdir {
let mut param = std::ffi::OsString::from("stylesdir=");
param.push(stylesdir);
cmd.arg("-a").arg(param);
}
if let Some(ref stylesheet) = self.asciidoctor_stylesheet {
let mut param = std::ffi::OsString::from("stylesheet=");
param.push(stylesheet);
cmd.arg("-a").arg(param);
}
cmd.arg(&asciidoc_file);
debug!("Running command {:?}", cmd);
cmd.output().expect("Couldn't run asciidoctor command");
Ok(())
}
}
#[derive(Template)]
#[template(path = "index.asciidoc", escape = "none")]
struct Index {
pub device: ommui_data::Device,
pub company_name: String,
}