swactor/crates/simulation/build.rs

74 lines
2.2 KiB
Rust
Raw Normal View History

//! Derive the runtime surface descriptor from the trait declarations
//! in `src/runtime/mod.rs`.
//!
//! Parsing the source with `syn` at build time means the descriptor
//! cannot drift: a signature edit that does not also touch
//! `surface.lock` fails `tests/surface_locked.rs`.
//!
//! Output goes to `$OUT_DIR/surface_descriptor.txt` and is consumed by
//! `src/runtime/mod.rs` via `include_str!`.
use std::env;
use std::fs;
use std::path::PathBuf;
use quote::ToTokens;
const RUNTIME_SRC: &str = "src/runtime/mod.rs";
fn main() {
println!("cargo:rerun-if-changed={RUNTIME_SRC}");
println!("cargo:rerun-if-changed=build.rs");
let src = fs::read_to_string(RUNTIME_SRC).expect("read runtime source");
let file = syn::parse_file(&src).expect("parse runtime source");
let descriptor = build_descriptor(&file);
let out_dir = PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR not set"));
fs::write(out_dir.join("surface_descriptor.txt"), descriptor)
.expect("write surface_descriptor.txt");
}
fn build_descriptor(file: &syn::File) -> String {
let mut out = String::new();
out.push_str("runtime surface descriptor v1\n");
let traits_mod = file
.items
.iter()
.find_map(|item| match item {
syn::Item::Mod(m) if m.ident == "traits" => Some(m),
_ => None,
})
.expect("`pub mod traits` not found in runtime source");
let (_, items) = traits_mod
.content
.as_ref()
.expect("`pub mod traits` is declaration-only; expected inline body");
for item in items {
if let syn::Item::Trait(t) = item {
out.push_str("trait ");
out.push_str(&t.ident.to_string());
out.push('\n');
for trait_item in &t.items {
if let syn::TraitItem::Fn(f) = trait_item {
let sig = normalize_whitespace(&f.sig.to_token_stream().to_string());
out.push_str(" ");
out.push_str(&sig);
out.push('\n');
}
}
}
}
out
}
fn normalize_whitespace(s: &str) -> String {
s.split_whitespace().collect::<Vec<_>>().join(" ")
}