#![expect(
clippy::expect_used,
clippy::panic,
reason = "build scripts halt the compilation on failure; panics are the idiomatic exit path"
)]
use std::env;
use std::fmt::Write as _;
use std::fs;
use std::path::{Path, PathBuf};
#[derive(serde::Deserialize)]
struct Root {
opcodes: Vec<Op>,
}
#[derive(serde::Deserialize)]
struct Op {
code: u8,
mnemonic: String,
}
fn main() {
let manifest_dir = PathBuf::from(
env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR must be set by cargo"),
);
let yaml_path = manifest_dir.join("opcodes.yaml");
println!("cargo:rerun-if-changed={}", yaml_path.display());
let raw = fs::read_to_string(&yaml_path)
.unwrap_or_else(|e| panic!("failed to read {}: {e}", yaml_path.display()));
let parsed: Root = serde_yaml_ng::from_str(&raw)
.unwrap_or_else(|e| panic!("failed to parse {}: {e}", yaml_path.display()));
let out_dir = env::var_os("OUT_DIR").expect("OUT_DIR must be set by cargo");
let dest = Path::new(&out_dir).join("opcodes_yaml.rs");
let mut code = String::new();
code.push_str("// Auto-generated by build.rs from conformance/opcodes.yaml.\n");
code.push_str(
"// Do not edit; regenerate by touching opcodes.yaml and re-running `cargo build`.\n\n",
);
code.push_str("const YAML_OPCODES: &[(u8, &str)] = &[\n");
for op in &parsed.opcodes {
writeln!(code, " ({:#04X}, \"{}\"),", op.code, op.mnemonic)
.expect("writing to a String cannot fail");
}
code.push_str("];\n");
fs::write(&dest, code).unwrap_or_else(|e| panic!("failed to write {}: {e}", dest.display()));
}