use std::path::{Path, PathBuf};
use std::process::Command;
fn main() {
println!("cargo:rerun-if-changed=crisp.typed.manifest");
println!("cargo:rerun-if-changed=crisp.types");
let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").expect("manifest dir"));
let out_dir = PathBuf::from(std::env::var("OUT_DIR").expect("out dir"));
std::fs::create_dir_all(&out_dir).expect("create out dir");
compile_templates(&manifest_dir, &out_dir);
}
fn compile_templates(manifest_dir: &Path, output_dir: &Path) {
let typed_out_dir = output_dir.join("crisp_typed");
std::fs::create_dir_all(&typed_out_dir).expect("create typed out dir");
compile_batch(
manifest_dir,
&manifest_dir.join("crisp.typed.manifest"),
&manifest_dir.join("crisp.types"),
&typed_out_dir,
&output_dir.join("crisp.typed.cargo-deps"),
);
}
fn compile_batch(
manifest_dir: &Path,
manifest_path: &Path,
typed_manifest: &Path,
output_dir: &Path,
cargo_deps_path: &Path,
) {
let output = invoke_crispc(manifest_dir, manifest_path, typed_manifest, output_dir, cargo_deps_path)
.expect("failed to invoke crispc");
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
let stdout = String::from_utf8_lossy(&output.stdout);
panic!(
"template compilation failed for manifest {}\n{}\n{}",
manifest_path.display(),
stderr.trim(),
stdout.trim()
);
}
let deps = std::fs::read_to_string(cargo_deps_path).unwrap_or_else(|err| {
panic!(
"template compilation succeeded but dependency file '{}' could not be read: {err}",
cargo_deps_path.display()
)
});
for line in deps.lines().map(str::trim).filter(|line| !line.is_empty()) {
println!("cargo:rerun-if-changed={line}");
}
}
fn invoke_crispc(
manifest_dir: &Path,
manifest_path: &Path,
typed_manifest: &Path,
output_dir: &Path,
cargo_deps_path: &Path,
) -> std::io::Result<std::process::Output> {
let crisp_binary = resolve_crispc_binary(manifest_dir).ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::NotFound,
"could not find crispc; set CRISPC_BIN or vendor ext/crisp/bin/crispc",
)
})?;
let mut command = Command::new(crisp_binary);
command.args([
"--manifest",
&manifest_path.display().to_string(),
"--dialect",
"rust",
]);
command.args([
"--typed-context-manifest",
&typed_manifest.display().to_string(),
]);
command.args([
"--cargo-deps",
&cargo_deps_path.display().to_string(),
"--out",
&output_dir.display().to_string(),
]);
command.output()
}
fn resolve_crispc_binary(manifest_dir: &Path) -> Option<PathBuf> {
if let Ok(path) = std::env::var("CRISPC_BIN") {
let candidate = PathBuf::from(path);
if candidate.is_file() {
return Some(candidate);
}
}
for ancestor in manifest_dir.ancestors() {
for candidate in [
ancestor.join("ext/crisp/dist/bin/crispc"),
ancestor.join("ext/crisp/bin/crispc"),
ancestor.join("dist/bin/crispc"),
ancestor.join("bin/crispc"),
] {
if candidate.is_file() {
return Some(candidate);
}
}
}
None
}