1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
use {
    crate::*,
    serde::Serialize,
    std::{
        fs::File,
        io::{self, Read},
        path::PathBuf,
    },
};

pub const DOLL_JS: &str = include_str!("../rsc/dom-doll.js");
pub const VIS_JS: &str = include_str!("../rsc/vis-timeline-graph2d.min.js");
pub const VIS_CSS: &str = include_str!("../rsc/vis-timeline-graph2d.min.css");
pub const SQL_JS: &str = include_str!("../rsc/sql-wasm.js");
pub const SQL_WASM: &[u8] = include_bytes!("../rsc/sql-wasm.wasm");
pub const VIEWER_JS: &str = include_str!("../rsc/viewer.js");
pub const VIEWER_CSS: &str = include_str!("../rsc/viewer.css");

#[derive(Debug, Serialize)]
struct Conf<'b> {
    bench_name: &'b str,
    task_name: Option<&'b str>,
    gb_version: String,
}

pub struct HtmlViewer<'b> {
    conf: Conf<'b>,
}

impl<'b> HtmlViewer<'b> {

    pub fn new(bench_name: &'b str, task_name: Option<&'b str>) -> Self {
        Self {
            conf: Conf {
                bench_name,
                task_name,
                gb_version: env!("CARGO_PKG_VERSION").to_string(),
            },
        }
    }

    pub fn open_in_browser(&self) -> Result<(), GlassBenchError> {
        let (mut w, path) = make_temp_file()?;
        self.write_html(&mut w)?;
        open::that(path)?;
        Ok(())
    }

    pub fn write_html<W: io::Write>(&self, mut w: W) -> Result<(), GlassBenchError> {
        writeln!(w, "<!DOCTYPE HTML>")?;
        writeln!(w, "<html>")?;
        writeln!(w, "<head>")?;
        writeln!(w, "<meta charset=UTF-8>")?;
        writeln!(w, "<style type=text/css>{}</style>", VIEWER_CSS)?;
        writeln!(w, "<style type=text/css>{}</style>", VIS_CSS)?;
        writeln!(w, "<script>{}</script>", VIS_JS)?;
        writeln!(w, "<script>{}</script>", SQL_JS)?;
        writeln!(w, "<script>{}</script>", DOLL_JS)?;
        writeln!(w, "<script charset=UTF-8>{}</script>", VIEWER_JS)?;
        write_db(&mut w)?;
        writeln!(w, "</head>")?;
        writeln!(w, "<body>")?;
        writeln!(w, "<script>")?;
        writeln!(w, "const gb_conf = {}", serde_json::to_string(&self.conf)?)?;
        writeln!(
            w,
            r#"const sql_conf = {{ locateFile: filename=>"data:application/wasm;base64,{}" }};"#,
            base64::encode(SQL_WASM),
        )?;
        writeln!(w, "main(sql_conf);")?;
        writeln!(w, "</script>")?;
        writeln!(w, "</body>")?;
        writeln!(w, "</html>")?;
        Ok(())
    }
}

pub fn make_temp_file() -> io::Result<(File, PathBuf)> {
    tempfile::Builder::new()
        .prefix("glassbench-")
        .suffix(".html")
        .rand_bytes(12)
        .tempfile()?
        .keep()
        .map_err(|_| io::Error::new(
            io::ErrorKind::Other,
            "temp file can't be kept",
        ))
}

pub fn write_db<W: io::Write>(mut w: W) -> Result<(), GlassBenchError> {
    let mut file = File::open(Db::path()?)?;
    let mut bytes = Vec::new();
    file.read_to_end(&mut bytes)?;
    writeln!(w,
        r#"<script>const db64="{}"</script>"#,
        base64::encode(&bytes),
    )?;
    Ok(())
}