cargo-html 0.2.1

create self-contained HTML programs
use super::util::*;
use crate::*;

use std::io::Write;
use std::path::*;



const HTML_SCRIPTS_PLACEHOLDER : &'static str = "<!-- SCRIPTS -->";

pub(crate) fn pages(args: &Arguments, metadata: &Metadata) {
    header("Building HTML pages");

    for config in args.configs.iter().copied() {
        let target_html_dir = metadata.target_directory().join("cargo-html").join(config.as_str());
        std::fs::create_dir_all(&target_html_dir).unwrap_or_else(|err| fatal!("unable to create `{}`: {}", target_html_dir.display(), err));

        let target_arch_config_dir = metadata.target_directory().join("wasm32-wasi").join(config.as_str());
        for (ty, target, pkg) in metadata.selected_targets_wasi() {
            let target_arch_config_dir = match ty {
                TargetType::Bin     => target_arch_config_dir.clone(),
                TargetType::Example => target_arch_config_dir.join("examples"),
                TargetType::Cdylib  => continue, // XXX?
            };

            let mut template_js = String::new();
            if pkg.wasm_bindgen.is_some() {
                let js_dir = target_arch_config_dir.join("js");
                js::inline_wasm_bindgen_bundler_importer(&mut template_js, "__cargo_html_wasmbindgen_bundler_js", &js_dir, target).unwrap_or_else(|err| fatal!("unable to reprocess wasm-bindgen javascript: {}", err));
                template_js.push_str("\r\n");
            }
            template_js.push_str(include_str!("../../template/script.js"));

            let wasm = target_arch_config_dir.join(format!("{}.async.wasm", target));
            generate(pkg, &target_html_dir, target, config, "console", &template_js, &wasm);
        }

        let target_arch_config_dir  = metadata.target_directory().join("wasm32-unknown-unknown").join(config.as_str());
        for (ty, target, pkg) in metadata.selected_targets_cargo_web() {
            let target_arch_config_dir = match ty {
                TargetType::Bin     => target_arch_config_dir.clone(),
                TargetType::Example => target_arch_config_dir.join("examples"),
                TargetType::Cdylib  => continue,
            };
            let package_js = target_arch_config_dir.join(format!("{}.js", target));
            let package_js = std::fs::read_to_string(&package_js).unwrap_or_else(|err| fatal!("unable to read `{}`: {}", package_js.display(), err));
            let package_js = include_str!("../../template/js/cargo-web.js").replace("{PACKAGE_JS}", &package_js);
            let wasm = target_arch_config_dir.join(format!("{}.wasm", target));
            generate(pkg, &target_html_dir, target, config, "basic", &package_js, &wasm);
        }

        let pkg_dir = metadata.target_directory().join("wasm32-unknown-unknown").join(config.as_str()).join("pkg");
        for (ty, target, pkg) in metadata.selected_targets_wasm_pack() {
            match ty {
                TargetType::Bin     => continue,
                TargetType::Example => continue,
                TargetType::Cdylib  => {},
            }
            let lib_name = target.replace("-", "_");
            let package_js = pkg_dir.join(format!("{}.js", lib_name));
            let package_js = std::fs::read_to_string(&package_js).unwrap_or_else(|err| fatal!("unable to read `{}`: {}", package_js.display(), err));
            let package_js = include_str!("../../template/js/wasm-pack.js").replace("{PACKAGE_JS}", &package_js);
            let wasm = pkg_dir.join(format!("{}_bg.wasm", lib_name));
            generate(pkg, &target_html_dir, target, config, "basic", &package_js, &wasm);
        }
    }
}

fn generate(
    package:            &Package,
    target_html_dir:    &Path,
    target:             &str,
    config:             Config,
    template_html:      &str,
    js_code:            &str,
    wasm:               &Path,
) {
    force_header();
    let target_html = target_html_dir.join(format!("{}.html", target));
    status!("Generating", "{}", target_html.display());

    let wasm = std::fs::read(&wasm).unwrap_or_else(|err| fatal!("unable to read `{}`: {}", wasm.display(), err));
    let wasm = base64::encode(&wasm[..]);

    let template_html_buf;
    let template_html = match package.template.as_ref().map(|s| s.as_str()).unwrap_or(template_html) {
        "basic"     => include_str!("../../template/html/basic.html"),
        "console"   => include_str!("../../template/html/console.html"),
        "xterm"     => include_str!("../../template/html/xterm.html"),
        file_html   => {
            let lower = file_html.to_ascii_lowercase();
            if !(lower.ends_with(".html") || lower.ends_with(".htm")) {
                fatal!("package `{}` specified an invalid HTML template, {:?}.  Expected \"basic\", \"console\", \"xterm\", or \"some/file.html\".", package.name, file_html);
            }
            template_html_buf = std::fs::read_to_string(package.directory.join(file_html)).unwrap_or_else(|err| fatal!("unable to read template `{}` for package `{}`: {}", file_html, package.name, err));
            template_html_buf.as_str()
        }
    };

    let template_html = template_html
        .replace("{CONFIG}", config.as_str())
        .replace("{TARGET_NAME}", target)
        .replace("<!-- STYLES -->", concat!("<style>\n", include_str!("../../template/css/style.css"), "\n</style>"))
        ;
    let scripts_placeholder_idx = template_html.find(HTML_SCRIPTS_PLACEHOLDER).expect("template missing `<!-- SCRIPTS -->` placeholder");

    mmrbi::fs::write_if_modified_with(target_html, |o| {
        let target_wasm = format!("{}.wasm", target);

        write!(o, "{}", &template_html[..scripts_placeholder_idx])?;
        writeln!(o, "<script>")?;
        writeln!(o, "        const CARGO_HTML_SETTINGS = {};", serde_json::to_string(&package.settings.wasi).unwrap())?;
        writeln!(o, "        CARGO_HTML_SETTINGS.env = CARGO_HTML_SETTINGS.env || {{}};")?;
        writeln!(o, "        {}", js_code)?;
        writeln!(o, "        mount_wasm_base64({:?}, {:?});", target_wasm, wasm)?;
        // TODO: mount filesystem
        writeln!(o, "        launch_wasm({:?}, {:?});", target_wasm, wasm)?;
        writeln!(o, "    </script>")?;
        write!(o, "    {}", &template_html[(scripts_placeholder_idx + HTML_SCRIPTS_PLACEHOLDER.len())..])?;
        Ok(())
    }).unwrap_or_else(|err| fatal!("unable to fully write HTML file: {}", err));
}