use std::collections::HashMap;
use lazy_static::lazy_static;
use serde_json::to_value;
use tera::Tera;
#[derive(Clone)]
pub struct SinglePageApplication {
pub view_name: String
}
lazy_static! {
pub static ref TEMPLATES: Tera = {
let mut tera = match Tera::new("views/**/*.html") {
Ok(t) => t,
Err(e) => {
println!("Parsing error(s): {}", e);
::std::process::exit(1);
}
};
tera.register_function("bundle", InjectBundle);
tera.autoescape_on(vec![]);
tera
};
pub static ref VITE_MANIFEST: ViteManifest = {
load_manifest_entries()
};
}
pub const DEFAULT_TEMPLATE: &'static str = "index.html";
pub fn to_template_name(request_path: &str) -> &'_ str {
let request_path = request_path.strip_prefix("/").unwrap();
return if request_path.eq("") { DEFAULT_TEMPLATE } else { request_path }
}
struct InjectBundle;
impl tera::Function for InjectBundle {
fn call(&self, args: &HashMap<String, serde_json::Value>) -> tera::Result<serde_json::Value> {
match args.get("name") {
Some(val) => return match tera::from_value::<String>(val.clone()) {
Ok(_bundle_name) => {
#[allow(unused_assignments)]
let mut inject: String = String::new();
#[cfg(not(debug_assertions))] {
let manifest_entry = VITE_MANIFEST.get(&_bundle_name)
.expect(&format!("could not get bundle `{_bundle_name}`"));
let entry_file = format!(r#"<script type="module" src="/{file}"></script>"#, file=manifest_entry.file);
let css_files = manifest_entry.css.as_ref().unwrap_or(&vec![]).into_iter().map(|css_file| {
format!(r#"<link rel="stylesheet" href="/{file}">"#, file=css_file)
}).collect::<Vec<String>>().join("\n");
inject = format!(r##"
<!-- Production Mode -->
{entry_file}
{css_files}
"##);
}
#[cfg(debug_assertions)] {
inject = format!(r#"
<script type="module" src="http://localhost:3000/@vite/client"></script>
<script type="module" src="http://localhost:3000/{_bundle_name}"></script>"#);
}
Ok(to_value(&inject).unwrap())
},
Err(_) => panic!("No bundle named '{:#?}'", val),
},
None => Err("oops".into()),
}
}
fn is_safe(&self) -> bool {
true
}
}
#[derive(serde::Deserialize)]
#[allow(dead_code, non_snake_case)]
pub struct ViteManifestEntry {
file: String,
dynamicImports: Option<Vec<String>>,
css: Option<Vec<String>>,
isEntry: Option<bool>,
isDynamicEntry: Option<bool>
}
type ViteManifest = HashMap<String, ViteManifestEntry>;
fn load_manifest_entries() -> ViteManifest {
let mut manifest: ViteManifest = HashMap::new();
use serde_json::Value;
let manifest_json = serde_json::from_str(std::fs::read_to_string(std::path::PathBuf::from("./public/manifest.json")).unwrap().as_str()).unwrap();
match manifest_json {
Value::Object(obj) => {
obj.keys().for_each(|manifest_key| {
let details = obj.get(manifest_key).unwrap();
let manifest_entry = serde_json::from_value::<ViteManifestEntry>(details.clone())
.expect("invalid vite manifest (or perhaps the create-rust-app parser broke!)");
manifest.insert(manifest_key.to_string(), manifest_entry);
});
},
_ => {
panic!("invalid vite manifest (or perhaps the create-rust-app parser broke!)");
}
}
manifest
}