fn main() {
println!("cargo:rerun-if-env-changed=INCLUDED_QT_TYPES");
let out_dir = std::env::var("OUT_DIR").expect("OUT_DIR not set");
let mut entries: Vec<(String, String)> = vec![];
if let Ok(paths_str) = std::env::var("INCLUDED_QT_TYPES") {
for path_str in paths_str.split(',') {
let path_str = path_str.trim();
if path_str.is_empty() {
continue;
}
println!("cargo:rerun-if-changed={path_str}");
let json = std::fs::read_to_string(path_str)
.unwrap_or_else(|e| panic!("INCLUDED_QT_TYPES: cannot read '{path_str}': {e}"));
let version = extract_version_from_json(&json)
.or_else(|| extract_version_from_filename(path_str))
.unwrap_or_else(|| {
panic!(
"Cannot detect Qt version from '{path_str}'. \
Use a filename like qt_types_6.3.2.json, or run \
`generate-qt-types` which adds a _version field."
)
});
let safe_id = version.replace('.', "_");
let dest_name = format!("qt_builtin_{safe_id}.json");
let dest_path = format!("{out_dir}/{dest_name}");
std::fs::write(&dest_path, &json).unwrap_or_else(|e| panic!("Cannot write '{dest_path}': {e}"));
println!("cargo:warning=Embedded Qt {version} types from {path_str}");
entries.push((version, dest_name));
}
}
let mut code = String::from(
"/// Built-in Qt type databases compiled into the binary.\n\
/// Each entry: (version_string, json_content).\n\
pub static BUILTIN_QT_TYPES: &[(&str, &str)] = &[\n",
);
for (version, filename) in &entries {
code.push_str(&format!(
" ({version:?}, include_str!(concat!(env!(\"OUT_DIR\"), \"/{filename}\"))),\n"
));
}
code.push_str("];\n");
let out_rs = format!("{out_dir}/qt_types_builtin.rs");
std::fs::write(&out_rs, &code).unwrap_or_else(|e| panic!("Cannot write '{out_rs}': {e}"));
}
fn extract_version_from_json(json: &str) -> Option<String> {
let start = json.find("\"_version\"")?;
let after = json[start + "\"_version\"".len()..].trim_start();
let after = after.strip_prefix(':')?;
let after = after.trim_start().strip_prefix('"')?;
let end = after.find('"')?;
let ver = &after[..end];
ver.contains('.').then(|| ver.to_string())
}
fn extract_version_from_filename(path: &str) -> Option<String> {
let stem = std::path::Path::new(path).file_stem()?.to_str()?;
for seg in stem.split('_') {
let parts: Vec<&str> = seg.split('.').collect();
if parts.len() >= 2
&& parts
.iter()
.all(|p| !p.is_empty() && p.chars().all(|c| c.is_ascii_digit()))
{
return Some(seg.to_string());
}
}
None
}