use std::env;
use std::error::Error;
use std::fmt::Write as _;
use std::fs;
use std::path::{Path, PathBuf};
use sdl3_sys::metadata::{self, Hint, Property, PropertyType as MetadataPropertyType};
fn main() {
#[cfg(any(target_os = "linux", target_os = "openbsd", target_os = "freebsd"))]
println!(r"cargo:rustc-link-search=/usr/local/lib");
if let Err(err) = generate_metadata_sources() {
panic!("failed to generate metadata sources: {err}");
}
}
fn generate_metadata_sources() -> Result<(), Box<dyn Error>> {
let out_dir = PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR is not set"));
println!("cargo:rerun-if-changed=build.rs");
generate_hint_names(&out_dir)?;
generate_property_names(&out_dir)?;
Ok(())
}
fn generate_hint_names(out_dir: &Path) -> Result<(), Box<dyn Error>> {
let mut output = String::new();
output.push_str("// @generated by build.rs; do not edit.\n\n");
let mut identifiers = Vec::new();
for hint in metadata::HINTS {
identifiers.push(generate_hint_definition(&mut output, hint)?);
}
writeln!(output, "pub const ALL: [&str; {}] = [", identifiers.len())?;
for ident in &identifiers {
writeln!(output, " {ident},")?;
}
output.push_str("];\n");
fs::write(out_dir.join("sdl3_hint_names.rs"), output)?;
Ok(())
}
fn generate_hint_definition(buf: &mut String, hint: &Hint) -> Result<String, Box<dyn Error>> {
if let Some(doc) = hint.doc {
append_doc_comment(buf, doc);
}
writeln!(buf, "#[doc(alias = {:?})]", hint.name)?;
writeln!(
buf,
"pub const {}: &str = {:?};",
hint.short_name,
hint.value_str()
)?;
buf.push('\n');
Ok(hint.short_name.to_string())
}
fn generate_property_names(out_dir: &Path) -> Result<(), Box<dyn Error>> {
let mut output = String::new();
output.push_str("// @generated by build.rs; do not edit.\n");
output.push_str("use super::{PropertyName, PropertyType, VersionTriple};\n\n");
let mut identifiers = Vec::new();
for property in metadata::PROPERTIES {
identifiers.push(generate_property_definition(&mut output, property)?);
}
output.push_str("pub const ALL: &[PropertyName] = &[\n");
for ident in &identifiers {
writeln!(output, " {ident},")?;
}
output.push_str("];\n");
fs::write(out_dir.join("sdl3_property_names.rs"), output)?;
Ok(())
}
fn generate_property_definition(
buf: &mut String,
property: &Property,
) -> Result<String, Box<dyn Error>> {
if let Some(doc) = property.doc {
append_doc_comment(buf, doc);
}
writeln!(buf, "#[doc(alias = {:?})]", property.name)?;
writeln!(
buf,
"pub const {}: PropertyName = PropertyName {{",
property.short_name
)?;
writeln!(buf, " module: {:?},", property.module)?;
writeln!(buf, " name: {:?},", property.name)?;
writeln!(buf, " short_name: {:?},", property.short_name)?;
writeln!(buf, " value: {:?},", property.value_str())?;
writeln!(
buf,
" raw: crate::sys::{}::{},",
property.module, property.name
)?;
writeln!(buf, " ty: {},", property_type_path(property.ty))?;
if let Some(doc) = property.doc {
writeln!(buf, " doc: Some({:?}),", doc)?;
} else {
buf.push_str(" doc: None,\n");
}
if let Some(version) = property.available_since {
let (major, minor, patch) = version_tuple(version);
writeln!(
buf,
" available_since: Some(VersionTriple {{ major: {major}, minor: {minor}, patch: {patch} }}),"
)?;
} else {
buf.push_str(" available_since: None,\n");
}
buf.push_str("};\n\n");
Ok(property.short_name.to_string())
}
fn append_doc_comment(buf: &mut String, doc: &str) {
for line in doc.trim_end().lines() {
if line.is_empty() {
buf.push_str("///\n");
} else {
buf.push_str("/// ");
buf.push_str(line);
buf.push('\n');
}
}
}
fn version_tuple(version: i32) -> (i32, i32, i32) {
let major = version / 1_000_000;
let minor = (version / 1_000) % 1_000;
let patch = version % 1_000;
(major, minor, patch)
}
fn property_type_path(ty: MetadataPropertyType) -> &'static str {
if ty == MetadataPropertyType::INVALID {
"PropertyType::INVALID"
} else if ty == MetadataPropertyType::POINTER {
"PropertyType::POINTER"
} else if ty == MetadataPropertyType::STRING {
"PropertyType::STRING"
} else if ty == MetadataPropertyType::NUMBER {
"PropertyType::NUMBER"
} else if ty == MetadataPropertyType::FLOAT {
"PropertyType::FLOAT"
} else if ty == MetadataPropertyType::BOOLEAN {
"PropertyType::BOOLEAN"
} else {
panic!("unknown property type {}", ty.0);
}
}