#![allow(dead_code)] use std::{ collections::HashMap, fs, path::PathBuf, sync::{LazyLock, OnceLock}, }; use askama::Template; use blake3::{Hash, Hasher}; use tracing_subscriber::EnvFilter; mod common; #[cfg(feature = "bbs")] mod bbs; #[cfg(feature = "ddd")] mod ddd; #[cfg(feature = "kh1")] mod kh1; #[cfg(feature = "kh2")] mod kh2; #[cfg(feature = "kh3")] mod kh3; pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub const ASSETS_FOLDER_PATH: &str = "./public/assets"; pub const SCRIPTS_FOLDER_PATH: &str = "./public/scripts"; pub const STYLES_FOLDER_PATH: &str = "./public/styles"; static BASE_URL: OnceLock = OnceLock::new(); static FILE_HASHES: LazyLock> = LazyLock::new(|| { let mut map = HashMap::new(); fn parse_path(path: PathBuf, map: &mut HashMap) { if let Ok(paths) = fs::read_dir(path) { for path in paths.flatten() { let path = path.path(); if path.metadata().is_ok_and(|p| p.is_dir()) { parse_path(path, map); continue; } let dir = path.parent().unwrap().to_str().unwrap(); let path = path.to_str().unwrap(); let is_module = path.contains("/common"); let mut hasher = Hasher::new(); hash_file(path.to_string().into(), &mut hasher); if !is_module { hash_files_in_dir(format!("{dir}/common").into(), &mut hasher); } let hash = hasher.finalize(); map.insert(path.to_string(), hash); } } } parse_path(SCRIPTS_FOLDER_PATH.into(), &mut map); parse_path(STYLES_FOLDER_PATH.into(), &mut map); map }); pub trait RuntimeModule { fn start_module(); } #[derive(Template)] #[template(path = "pages/index.html")] struct IndexTemplate {} fn main() { // Initialize tracing tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) .event_format(tracing_subscriber::fmt::format().with_source_location(true)) .init(); tracing::info!("Generating the main menu template"); let template = IndexTemplate {}; std::fs::write("./out/index.html", template.render().unwrap()).unwrap(); std::process::Command::new("cp") .args(["-r", "./assets", "./out"]) .output() .unwrap(); #[cfg(feature = "bbs")] start_module::(); #[cfg(feature = "ddd")] start_module::(); #[cfg(feature = "kh1")] start_module::(); #[cfg(feature = "kh2")] start_module::(); #[cfg(feature = "kh3")] start_module::(); } fn start_module() { M::start_module(); } fn base_url() -> String { BASE_URL .get_or_init(|| std::env::var("BASE_URL").unwrap_or("".to_string())) .to_string() } fn find_hash(file: &str) -> String { let map = &*FILE_HASHES; let base_url = base_url(); let hash = map.get(&format!(".{file}")); if let Some(hash) = hash { return format!("{base_url}{file}?hash={hash}"); } format!("{base_url}{file}") } fn hash_files_in_dir(path: PathBuf, hasher: &mut Hasher) { if let Ok(paths) = fs::read_dir(path) { for path in paths.flatten() { let path = path.path(); if path.metadata().is_ok_and(|p| p.is_dir()) { hash_files_in_dir(path, hasher); continue; } hash_file(path, hasher); } } } fn hash_file(path: PathBuf, hasher: &mut Hasher) { if let Ok(file) = fs::read(path) { hasher.update(&file); } } fn create_file(dir: &str, file: &str, buf: impl Template) -> std::io::Result<()> { std::fs::create_dir_all(dir)?; std::fs::write(format!("{}/{}.html", dir, file), buf.render().unwrap())?; Ok(()) }