use minify_html::{Cfg, minify}; use std::env; use std::fs; use std::io; use std::path::Path; const HIGHLIGHT_STYLE_URL_TEMPLATE: &str = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/{VERSION}/styles/atom-one-dark.min.css"; const HIGHLIGHT_SCRIPT_URL_TEMPLATE: &str = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/{VERSION}/highlight.min.js"; const MERMAID_SCRIPT_URL_TEMPLATE: &str = "https://cdn.jsdelivr.net/npm/mermaid@{VERSION}/dist/mermaid.min.js"; fn main() { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=assets/"); println!("cargo:rerun-if-env-changed=FORCE_DEPS_DOWNLOAD"); println!("cargo:rerun-if-changed=templates/"); minify_template_file("templates/base.html", "templates/base.min.html").unwrap(); minify_template_file("templates/dir.html", "templates/dir.min.html").unwrap(); minify_template_file("templates/file.html", "templates/file.min.html").unwrap(); let out_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); let assets_path = Path::new(&out_dir).join("assets"); fs::create_dir_all(&assets_path).expect("Fail to create directory"); let highlight_version = match env::var("HIGHLIGHT_VERSION") { Ok(var) => var, Err(_) => "11.9.0".to_string(), }; let mermaid_version = match env::var("MERMAID_VERSION") { Ok(var) => var, Err(_) => "10".to_string(), }; download( HIGHLIGHT_STYLE_URL_TEMPLATE, &highlight_version, &assets_path.join("highlight.css"), ); download( HIGHLIGHT_SCRIPT_URL_TEMPLATE, &highlight_version, &assets_path.join("highlight.js"), ); download( MERMAID_SCRIPT_URL_TEMPLATE, &mermaid_version, &assets_path.join("mermaid.js"), ); } fn download(url: &str, version: &str, dest_path: &Path) { if !should_download(dest_path) { println!( "cargo:warning=✅ {} actual, skip downloading", dest_path.to_string_lossy() ); return; } println!( "cargo:warning=📥 Downloading {}...", dest_path.to_string_lossy() ); let response = reqwest::blocking::get(url.replace("{VERSION}", version)) .expect("Fail to download sources") .error_for_status() .expect("Fail to download sources: non-2xx status code"); let content = response.text().expect("Fail to read answer"); fs::write(dest_path, &content).expect("Fail to write file"); } fn should_download(path: &Path) -> bool { if env::var("FORCE_DEPS_DOWNLOAD").is_ok() { return true; } if path.exists() { return false; } true } fn minify_template_file>(input_path: P, output_path: P) -> io::Result<()> { let html_content = fs::read_to_string(input_path.as_ref())?; let minified = minify_template_content(&html_content) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; fs::write(output_path.as_ref(), minified)?; Ok(()) } #[allow(clippy::missing_errors_doc)] pub fn minify_template_content(template: &str) -> Result { let mut cfg = Cfg::new(); cfg.allow_removing_spaces_between_attributes = true; cfg.keep_comments = false; cfg.minify_css = true; cfg.minify_js = true; cfg.keep_html_and_head_opening_tags = true; cfg.keep_closing_tags = true; cfg.remove_processing_instructions = true; let minified_bytes = minify(template.as_bytes(), &cfg); String::from_utf8(minified_bytes).map_err(|e| format!("Invalid UTF-8 in output: {e}")) }