use std::collections::HashMap;
use std::path::PathBuf;
use std::process::Command;
fn main() {
let manifest_dir =
PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"));
let pin_path = manifest_dir.join("hyperd-version.toml");
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed={}", pin_path.display());
println!("cargo:rerun-if-changed=../.git/HEAD");
println!("cargo:rerun-if-changed=../.git/refs/heads");
println!("cargo:rerun-if-changed=../.git/index");
let hash = Command::new("git")
.args(["rev-parse", "--short=8", "HEAD"])
.output()
.ok()
.filter(|o| o.status.success())
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
.filter(|s| !s.is_empty())
.unwrap_or_else(|| "unknown".to_string());
let dirty = Command::new("git")
.args([
"status",
"--porcelain",
"-uno",
"--",
":(exclude,top)Cargo.lock",
])
.output()
.ok()
.filter(|o| o.status.success())
.is_some_and(|o| !o.stdout.is_empty());
let git_hash = if dirty && hash != "unknown" {
format!("{hash}-dirty")
} else {
hash
};
println!("cargo:rustc-env=HYPERDB_GIT_HASH={git_hash}");
let text = std::fs::read_to_string(&pin_path)
.unwrap_or_else(|e| panic!("failed to read {}: {e}", pin_path.display()));
let pin: PinCheck =
toml::from_str(&text).unwrap_or_else(|e| panic!("invalid {}: {e}", pin_path.display()));
assert!(
!pin.version.trim().is_empty(),
"{}: `version` is empty",
pin_path.display()
);
assert!(
!pin.build_id.trim().is_empty(),
"{}: `build_id` is empty",
pin_path.display()
);
assert!(
!pin.version.contains(char::is_whitespace),
"{}: `version` contains whitespace",
pin_path.display()
);
assert!(
!pin.build_id.contains(char::is_whitespace),
"{}: `build_id` contains whitespace",
pin_path.display()
);
const SUPPORTED: &[&str] = &[
"macos-arm64",
"macos-x86_64",
"linux-x86_64",
"windows-x86_64",
];
for key in pin.sha256.keys() {
assert!(
SUPPORTED.contains(&key.as_str()),
"{}: unknown platform key `{}` in [sha256]; supported: {:?}",
pin_path.display(),
key,
SUPPORTED
);
}
for (plat, sha) in &pin.sha256 {
let trimmed = sha.trim();
if trimmed.is_empty() {
println!(
"cargo:warning=hyperd-version.toml: sha256 for `{plat}` is empty; downloads will skip verification"
);
continue;
}
assert!(
trimmed.len() == 64 && trimmed.chars().all(|c| c.is_ascii_hexdigit()),
"{}: sha256 for `{}` is not a 64-char hex string (got {:?})",
pin_path.display(),
plat,
trimmed
);
}
}
#[derive(serde::Deserialize)]
struct PinCheck {
version: String,
build_id: String,
#[serde(default)]
sha256: HashMap<String, String>,
}