ommui_data 0.39.0

OMMUI data structures
Documentation
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,
    /// The path where the documentation gets generated.
    #[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)");
                        // The width and height element in the svg xml
                        // are missing, so we add them.
                        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,
}