Files
rustpad/build.rs
T
2026-10-29 13:07:22 +01:00

99 lines
3.2 KiB
Rust

use std::{
env, fs,
path::{Path, PathBuf},
};
const FNV_OFFSET: u64 = 0xcbf29ce484222325;
const FNV_PRIME: u64 = 0x100000001b3;
fn hash_bytes(mut hash: u64, bytes: &[u8]) -> u64 {
for byte in bytes {
hash ^= u64::from(*byte);
hash = hash.wrapping_mul(FNV_PRIME);
}
hash
}
fn collect_files(root: &Path, dir: &Path, files: &mut Vec<PathBuf>) {
let mut entries = fs::read_dir(dir)
.unwrap_or_else(|error| panic!("failed to read {}: {error}", dir.display()))
.filter_map(Result::ok)
.map(|entry| entry.path())
.collect::<Vec<_>>();
entries.sort();
for path in entries {
if path.is_dir() {
collect_files(root, &path, files);
} else if path.is_file() {
println!(
"cargo:rerun-if-changed={}",
path.strip_prefix(root).unwrap_or(&path).display()
);
files.push(path);
}
}
}
fn asset_version(manifest_dir: &Path) -> String {
let mut files = Vec::new();
collect_files(manifest_dir, &manifest_dir.join("static"), &mut files);
collect_files(manifest_dir, &manifest_dir.join("lang"), &mut files);
files.sort();
let mut hash = FNV_OFFSET;
for path in files {
let relative = path.strip_prefix(manifest_dir).unwrap_or(&path);
hash = hash_bytes(hash, relative.to_string_lossy().as_bytes());
hash = hash_bytes(hash, &[0]);
let bytes = fs::read(&path)
.unwrap_or_else(|error| panic!("failed to read {}: {error}", path.display()));
hash = hash_bytes(hash, &bytes);
hash = hash_bytes(hash, &[0xff]);
}
format!(
"{}-{hash:016x}",
env::var("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION")
)
}
fn main() {
println!("cargo:rerun-if-changed=lang");
println!("cargo:rerun-if-changed=static");
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"));
let lang_dir = manifest_dir.join("lang");
let mut files = fs::read_dir(&lang_dir)
.unwrap_or_else(|error| panic!("failed to read {}: {error}", lang_dir.display()))
.filter_map(Result::ok)
.map(|entry| entry.path())
.filter(|path| path.extension().and_then(|value| value.to_str()) == Some("json"))
.collect::<Vec<_>>();
files.sort();
if files.is_empty() {
panic!("lang directory must contain at least one .json language file");
}
let mut generated = String::from("pub const EMBEDDED_LANGUAGE_FILES: &[(&str, &str)] = &[\n");
for path in files {
let name = path
.file_name()
.and_then(|value| value.to_str())
.expect("language filename must be valid UTF-8");
println!("cargo:rerun-if-changed=lang/{name}");
generated.push_str(&format!(
" ({name:?}, include_str!(concat!(env!(\"CARGO_MANIFEST_DIR\"), \"/lang/{name}\"))),\n"
));
}
generated.push_str("];\n");
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR"));
fs::write(out_dir.join("languages.rs"), generated)
.expect("failed to generate embedded languages");
println!(
"cargo:rustc-env=RUSTPAD_ASSET_VERSION={}",
asset_version(&manifest_dir)
);
}