| #[cfg(not(any(feature = "use-minified")))] |
| use sha2::{Digest, Sha256}; |
| #[cfg(not(any(feature = "use-minified")))] |
| use std::collections::HashMap; |
| #[cfg(not(any(feature = "use-minified")))] |
| use std::fs; |
| use std::io::Result; |
| #[cfg(not(any(feature = "use-minified")))] |
| use std::path::Path; |
| use std::path::PathBuf; |
| #[cfg(not(any(feature = "use-minified")))] |
| use std::process::Command; |
|
|
| |
| #[cfg(not(any(feature = "use-minified")))] |
| const SUPPORTED_EXTENSIONS: [&str; 4] = ["html", "js", "css", "md"]; |
|
|
| #[cfg(not(any(feature = "use-minified")))] |
| fn check_and_install_deps() -> Result<()> { |
| let scripts_dir = Path::new("scripts"); |
| let node_modules = scripts_dir.join("node_modules"); |
|
|
| if !node_modules.exists() { |
| println!("cargo:warning=Installing minifier dependencies..."); |
| let status = Command::new("npm") |
| .current_dir(scripts_dir) |
| .arg("install") |
| .status()?; |
|
|
| if !status.success() { |
| panic!("Failed to install npm dependencies"); |
| } |
| println!("cargo:warning=Dependencies installed successfully"); |
| } |
| Ok(()) |
| } |
|
|
| #[cfg(not(any(feature = "use-minified")))] |
| fn get_files_hash() -> Result<HashMap<PathBuf, String>> { |
| let mut file_hashes = HashMap::new(); |
| let static_dir = Path::new("static"); |
|
|
| |
| let readme_path = Path::new("README.md"); |
| if readme_path.exists() { |
| let content = fs::read(readme_path)?; |
| let mut hasher = Sha256::new(); |
| hasher.update(&content); |
| let hash = format!("{:x}", hasher.finalize()); |
| file_hashes.insert(readme_path.to_path_buf(), hash); |
| } |
|
|
| if static_dir.exists() { |
| for entry in fs::read_dir(static_dir)? { |
| let entry = entry?; |
| let path = entry.path(); |
|
|
| |
| if let Some(ext) = path.extension().and_then(|e| e.to_str()) { |
| if SUPPORTED_EXTENSIONS.contains(&ext) && !path.to_string_lossy().contains(".min.") |
| { |
| let content = fs::read(&path)?; |
| let mut hasher = Sha256::new(); |
| hasher.update(&content); |
| let hash = format!("{:x}", hasher.finalize()); |
| file_hashes.insert(path, hash); |
| } |
| } |
| } |
| } |
|
|
| Ok(file_hashes) |
| } |
|
|
| #[cfg(not(any(feature = "use-minified")))] |
| fn load_saved_hashes() -> Result<HashMap<PathBuf, String>> { |
| let hash_file = Path::new("scripts/.asset-hashes.json"); |
| if hash_file.exists() { |
| let content = fs::read_to_string(hash_file)?; |
| let hash_map: HashMap<String, String> = serde_json::from_str(&content)?; |
| Ok(hash_map |
| .into_iter() |
| .map(|(k, v)| (PathBuf::from(k), v)) |
| .collect()) |
| } else { |
| Ok(HashMap::new()) |
| } |
| } |
|
|
| #[cfg(not(any(feature = "use-minified")))] |
| fn save_hashes(hashes: &HashMap<PathBuf, String>) -> Result<()> { |
| let hash_file = Path::new("scripts/.asset-hashes.json"); |
| let string_map: HashMap<String, String> = hashes |
| .iter() |
| .map(|(k, v)| (k.to_string_lossy().into_owned(), v.clone())) |
| .collect(); |
| let content = serde_json::to_string_pretty(&string_map)?; |
| fs::write(hash_file, content)?; |
| Ok(()) |
| } |
|
|
| #[cfg(not(any(feature = "use-minified")))] |
| fn minify_assets() -> Result<()> { |
| |
| let current_hashes = get_files_hash()?; |
|
|
| if current_hashes.is_empty() { |
| println!("cargo:warning=No files to minify"); |
| return Ok(()); |
| } |
|
|
| |
| let saved_hashes = load_saved_hashes()?; |
|
|
| |
| let files_to_update: Vec<_> = current_hashes |
| .iter() |
| .filter(|(path, current_hash)| { |
| let is_readme = path.file_name().map_or(false, |f| f == "README.md"); |
| let ext = path.extension().and_then(|e| e.to_str()).unwrap_or(""); |
|
|
| |
| let min_path = if is_readme { |
| PathBuf::from("static/readme.min.html") |
| } else { |
| path.with_file_name(format!( |
| "{}.min.{}", |
| path.file_stem().unwrap().to_string_lossy(), |
| ext |
| )) |
| }; |
|
|
| |
| if !min_path.exists() { |
| return true; |
| } |
|
|
| |
| saved_hashes |
| .get(*path) |
| .map_or(true, |saved_hash| saved_hash != *current_hash) |
| }) |
| .map(|(path, _)| path.file_name().unwrap().to_string_lossy().into_owned()) |
| .collect(); |
|
|
| if files_to_update.is_empty() { |
| println!("cargo:warning=No files need to be updated"); |
| return Ok(()); |
| } |
|
|
| println!("cargo:warning=Minifying {} files...", files_to_update.len()); |
|
|
| |
| let status = Command::new("node") |
| .arg("scripts/minify.js") |
| .args(&files_to_update) |
| .status()?; |
|
|
| if !status.success() { |
| panic!("Asset minification failed"); |
| } |
|
|
| |
| save_hashes(¤t_hashes)?; |
|
|
| Ok(()) |
| } |
|
|
| fn main() -> Result<()> { |
| |
| println!("cargo:rerun-if-changed=src/chat/aiserver/v1/lite.proto"); |
| println!("cargo:rerun-if-changed=src/chat/config/key.proto"); |
| |
| let protoc_path = match std::env::var_os("PROTOC") { |
| Some(path) => PathBuf::from(path), |
| None => { |
| println!("cargo:warning=PROTOC environment variable not set, using default protoc."); |
| |
| PathBuf::new() |
| } |
| }; |
| let mut config = prost_build::Config::new(); |
| |
| if !protoc_path.as_os_str().is_empty() { |
| config.protoc_executable(protoc_path); |
| } |
| |
| config |
| .compile_protos( |
| &["src/chat/aiserver/v1/lite.proto"], |
| &["src/chat/aiserver/v1/"], |
| ) |
| .unwrap(); |
| config |
| .compile_protos(&["src/chat/config/key.proto"], &["src/chat/config/"]) |
| .unwrap(); |
|
|
| |
| println!("cargo:rerun-if-changed=scripts/minify.js"); |
| println!("cargo:rerun-if-changed=scripts/package.json"); |
| println!("cargo:rerun-if-changed=static/api.html"); |
| println!("cargo:rerun-if-changed=static/build_key.html"); |
| println!("cargo:rerun-if-changed=static/config.html"); |
| println!("cargo:rerun-if-changed=static/logs.html"); |
| println!("cargo:rerun-if-changed=static/shared-styles.css"); |
| println!("cargo:rerun-if-changed=static/shared.js"); |
| println!("cargo:rerun-if-changed=static/tokens.html"); |
| println!("cargo:rerun-if-changed=README.md"); |
|
|
| #[cfg(not(any(feature = "use-minified")))] |
| { |
| |
| check_and_install_deps()?; |
|
|
| |
| minify_assets()?; |
| } |
|
|
| Ok(()) |
| } |
|
|