alex/volcanic
Automated vulkan bindings
BRANCH main
SNAPSHOT Improved loading api and made ready for publish
DOWNLOAD
root codegen src output.rs
use crate::Result;
use std::io::Write;
use std::path::PathBuf;
use std::process::{Command, Stdio};
pub fn format_rust(source: &str) -> Result<String> {
let mut child = Command::new("rustfmt")
.args([
"--edition",
"2024",
"--emit",
"stdout",
"--config",
"skip_children=true",
])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
let mut stdin = child.stdin.take().expect("piped rustfmt stdin");
let output = std::thread::scope(|scope| -> Result<_> {
// Drain stdout while feeding large generated files to rustfmt
let writer = scope.spawn(move || stdin.write_all(source.as_bytes()));
let output = child.wait_with_output()?;
let write_result = writer.join().expect("rustfmt writer panicked");
if !output.status.success() {
return Err(format!(
"rustfmt failed: {}",
String::from_utf8_lossy(&output.stderr)
)
.into());
}
write_result?;
Ok(output)
})?;
Ok(String::from_utf8(output.stdout)?)
}
pub fn write_outputs(outputs: &[(PathBuf, String)], check: bool) -> Result<()> {
let mut stale = Vec::new();
for (path, expected) in outputs {
let current = match std::fs::read_to_string(path) {
Ok(current) => Some(current),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => None,
Err(error) => return Err(error.into()),
};
if current.as_deref() == Some(expected) {
continue;
}
if check {
stale.push(path.display().to_string());
} else {
std::fs::write(path, expected)?;
}
}
if stale.is_empty() {
Ok(())
} else {
Err(format!("generated files are stale: {}", stale.join(", ")).into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_reports_stale_files_without_writing() {
let path = std::env::temp_dir().join(format!(
"volcanic-output-test-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
let mut file = std::fs::File::create_new(&path).unwrap();
struct Cleanup(PathBuf);
impl Drop for Cleanup {
fn drop(&mut self) {
let _ = std::fs::remove_file(&self.0);
}
}
let _cleanup = Cleanup(path.clone());
file.write_all(b"old").unwrap();
drop(file);
let outputs = [(path.clone(), "new".to_string())];
let error = write_outputs(&outputs, true).unwrap_err();
assert!(error.to_string().contains("generated files are stale"));
assert_eq!(std::fs::read_to_string(&path).unwrap(), "old");
write_outputs(&outputs, false).unwrap();
assert_eq!(std::fs::read_to_string(&path).unwrap(), "new");
write_outputs(&outputs, true).unwrap();
}
#[test]
fn formatting_rejects_invalid_generated_rust() {
assert!(format_rust("fn invalid(").is_err());
}
}