use std::{
collections::HashMap,
env,
fs::File,
io::Write,
process::Command,
};
use wasm_bindgen_cli_support::{
Bindgen,
EncodeInto,
};
mod template;
mod template_rocket;
mod template_tide;
struct Context {
pub out_dir: String,
pub target_wasm_dir: String,
pub manifest_dir: String,
pub file: File,
}
pub fn build_module_to_wasm(
target_wasm_dir: &String,
module_name: &str,
path_to_manifest: &str,
js_file_name: &str,
wasm_file_name: &str,
html_file_name: &str,
theme: &str,
) {
let mut command = Command::new("cargo");
command.arg("build");
let profile;
match std::env::var("PROFILE") == Ok("release".to_owned()) {
true => {
command.arg("--release");
profile = "release";
},
false => {
profile = "debug";
},
}
command
.arg("-p")
.arg(module_name.clone())
.arg("--manifest-path")
.arg(path_to_manifest)
.arg("--target")
.arg("wasm32-unknown-unknown")
.env("RUSTFLAGS", "-D warnings")
.env("CARGO_TARGET_DIR", target_wasm_dir);
let status = command.status().expect("cargo failed to compile");
if !status.success() {
panic!("cargo build failed:{}", status);
}
let input_path = format!(
"{}/wasm32-unknown-unknown/{}/{}.wasm",
target_wasm_dir, profile, module_name
);
Bindgen::new()
.input_path(input_path)
.no_modules(true)
.unwrap()
.debug(false)
.demangle(true)
.keep_debug(false)
.remove_name_section(false)
.remove_producers_section(false)
.typescript(false)
.out_name("index")
.encode_into(EncodeInto::Always)
.generate(target_wasm_dir.clone())
.expect("bindgen can't process input");
let old_js = format!("{}/index.js", target_wasm_dir);
std::fs::copy(old_js.clone(), &js_file_name).expect(&format!(
"Couldn't copy js file {}/index.js to {}",
target_wasm_dir, js_file_name
));
std::fs::remove_file(old_js.clone()).expect(&format!("Couldn't remove {}", old_js));
let old_wasm = format!("{}/index_bg.wasm", target_wasm_dir);
std::fs::copy(old_wasm.clone(), &wasm_file_name).expect(&format!(
"Couldn't copy wasm file {}/index_gb.wasm to {}",
target_wasm_dir, wasm_file_name
));
std::fs::remove_file(old_wasm.clone())
.expect(&format!("Couldn't remove {}", old_wasm));
let html_content = template::HTML
.to_string()
.replace(template::JS_FILE_NAME_PLACEHOLDER, ".js")
.replace(template::WASM_FILE_NAME_PLACEHOLDER, "_bg.wasm")
.replace(template::CUSTOM_THEME_PLACEHOLDER, theme)
.replace("BODY_THEME_CLASS_NAME", crate::BODY_THEME_CLASS_NAME);
let mut file = File::create(html_file_name.clone())
.expect(&format!("Created html file {}", html_file_name));
file.write_all(html_content.as_bytes()).expect("html file write");
}
fn build_module(
module_name: &str,
path_to_manifest: &str,
module_template: &str,
context: &mut Context,
theme: &str,
) {
let html_file_name = format!("{}/{}.html", context.out_dir, module_name);
let wasm_file_name = format!("{}/{}.wasm", context.out_dir, module_name);
let js_file_name = format!("{}/{}.js", context.out_dir, module_name);
build_module_to_wasm(
&context.target_wasm_dir,
module_name,
path_to_manifest,
&js_file_name,
&wasm_file_name,
&html_file_name,
theme,
);
let file_content = module_template
.to_string()
.replace(template::WASM_FILE_NAME_PLACEHOLDER, &wasm_file_name)
.replace(template::JS_FILE_NAME_PLACEHOLDER, &js_file_name)
.replace(template::MODULE_NAME_PLACEHOLDER, module_name)
.replace(template::HTML_FILE_NAME_PLACEHOLDER, &html_file_name);
context.file.write_all(file_content.as_bytes()).expect("file content written");
}
pub(crate) fn build_all_inner(
pages: &[crate::SimpleUiPage],
framework: crate::Framework,
target_wasm_dir: String,
theme: &str,
) {
let prolog = match framework {
crate::Framework::Tide => "",
crate::Framework::Rocket => template_rocket::PROLOG,
};
let module_template = match framework {
crate::Framework::Tide => template_tide::MODULE,
crate::Framework::Rocket => template_rocket::MODULE,
};
let out_dir = env::var("OUT_DIR").expect("OUT_DIR have to be setted up");
let file_name = format!("{}/uni_build_generated.rs", out_dir);
let mut file = File::create(file_name).expect("file opened");
file.write_all(prolog.as_bytes()).expect("file content written");
let mut context = Context {
file,
out_dir,
target_wasm_dir,
manifest_dir: env::var("CARGO_MANIFEST_DIR")
.expect("$CARGO_MANIFEST_DIR not exist"),
};
let mut output = HashMap::new();
for page in pages {
build_module(
page.module_name(),
page.module_path(),
module_template,
&mut context,
theme,
);
if output.contains_key(page.path()) {
panic!("Two pages for the same path found. The path is `{}`", page.path());
}
output.insert(page.path().to_owned(), page.module_name().to_owned());
}
let (pre, module, post) = match framework {
crate::Framework::Rocket => {
(
template_rocket::GLOBAL_PRE,
template_rocket::GLOBAL_MODULE,
template_rocket::GLOBAL_POST,
)
},
crate::Framework::Tide => {
(
template_tide::GLOBAL_PRE,
template_tide::GLOBAL_MODULE,
template_tide::GLOBAL_POST,
)
},
};
context.file.write_all(pre.as_bytes()).expect("File write error");
for (path, name) in output.iter() {
let s = module
.to_string()
.replace(template::MODULE_NAME_PLACEHOLDER, &name)
.replace(template::MODULE_PAGE_PATH_PLACEHOLDER, &path);
context.file.write_all(s.as_bytes()).expect("File write error");
}
context.file.write_all(post.as_bytes()).expect("File write error");
}