datavzrd 2.64.0

A tool to create visual HTML reports from collections of CSV/TSV tables
use crate::spec::ItemsSpec;
use anyhow::Result;
use minify_js::{minify, Session, TopLevelMode};
use std::collections::HashMap;
use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use tera::{escape_html, Context, Tera};

pub(crate) fn render_static_files<P: AsRef<Path>>(path: P) -> Result<()> {
    let path = Path::new(path.as_ref()).join("static");
    fs::create_dir(&path)?;
    let bundle = include_str!(concat!(env!("OUT_DIR"), "/web/dist/bundle.js"));
    let mut out = File::create(path.join(Path::new("bundle.js")))?;
    out.write_all(bundle.as_bytes())?;
    Ok(())
}

pub(crate) fn minify_js(file: &str, debug: bool) -> Result<Vec<u8>> {
    if !debug {
        let mut minified: Vec<u8> = Vec::new();
        minify(
            &Session::new(),
            TopLevelMode::Global,
            file.as_bytes(),
            &mut minified,
        )
        .expect("Failed minifying js");
        Ok(minified)
    } else {
        Ok(file.as_bytes().to_vec())
    }
}

pub(crate) fn render_index_file<P: AsRef<Path>>(path: P, specs: &ItemsSpec) -> Result<()> {
    let mut templates = Tera::default();
    templates.add_raw_template(
        "index.html.tera",
        include_str!("../../../templates/index.html.tera"),
    )?;
    let views: HashMap<_, _> = specs
        .views
        .iter()
        .map(|(name, view)| (name, view.description.as_deref().map(escape_html)))
        .collect();
    let mut context = Context::new();
    context.insert("table", &specs.default_view);
    context.insert("views", &views);
    context.insert("version", &env!("CARGO_PKG_VERSION"));
    context.insert("name", &specs.report_name);
    let file_path = Path::new(path.as_ref()).join(Path::new("index").with_extension("html"));
    let html = templates.render("index.html.tera", &context)?;
    let mut file = fs::File::create(file_path)?;
    file.write_all(html.as_bytes())?;
    Ok(())
}

pub(crate) fn round(x: f32, decimals: u32) -> f32 {
    let y = 10i32.pow(decimals) as f32;
    (x * y).round() / y
}

#[cfg(test)]
mod tests {
    use crate::render_static_files;
    use std::fs;
    use std::path::Path;

    #[test]
    fn test_render_static_files() {
        render_static_files(Path::new("/tmp")).unwrap();
        let bundle = include_str!(concat!(env!("OUT_DIR"), "/web/dist/bundle.js"));

        let rendered_file_content = fs::read_to_string("/tmp/static/bundle.js")
            .expect("Could not read rendered bundle file.");
        assert_eq!(rendered_file_content, bundle);

        for entry in fs::read_dir("/tmp/static").unwrap() {
            fs::remove_file(entry.unwrap().path()).unwrap();
        }
        fs::remove_dir("/tmp/static").unwrap();
    }
}