use std::fs;
use std::io::Write;
use std::path::Path;
use std::process::Command;
use xsd_parser::config::{GeneratorFlags, InterpreterFlags, OptimizerFlags, Schema};
use xsd_parser::{generate, Config};
fn main() {
let gen_dir = Path::new("src/generated");
if std::env::var("DOCS_RS").is_err() {
Command::new("git")
.args(["submodule", "update", "--init", "--recursive"])
.output()
.expect("Sync submodules failed!");
let fixed_metadata_xsd: String = fs::read_to_string("dawproject/MetaData.xsd").unwrap();
let project_xsd: String = fs::read_to_string("dawproject/Project.xsd").unwrap();
let fixed_project_xsd = project_xsd.replace(
"<xs:attribute name=\"contentType\">",
"<xs:attribute name=\"contentTypes\">",
);
let fixed_project_xsd = fixed_project_xsd
.replace("\"eqBandType\"", "\"eqBandKind\"")
.replace("\"sendType\"", "\"sendKind\"");
fs::write("assets/FixedMetaData.xsd", &fixed_metadata_xsd).unwrap();
fs::write("assets/FixedProject.xsd", &fixed_project_xsd).unwrap();
}
generate_from_xsd(
"assets/FixedMetaData.xsd",
&gen_dir.join("metadata_generated.rs"),
);
generate_from_xsd(
"assets/FixedProject.xsd",
&gen_dir.join("project_generated.rs"),
);
println!("cargo:rerun-if-changed=assets/FixedMetaData.xsd");
println!("cargo:rerun-if-changed=assets/FixedProject.xsd");
println!("cargo:rerun-if-changed=dawproject/MetaData.xsd");
println!("cargo:rerun-if-changed=dawproject/Project.xsd");
}
fn generate_from_xsd(xsd_path: &str, output_path: &Path) {
let config = Config::default()
.with_schema(Schema::File(xsd_path.into()))
.with_interpreter_flags(InterpreterFlags::all())
.with_optimizer_flags(OptimizerFlags::all())
.with_generator_flags(GeneratorFlags::all())
.with_serde_quick_xml();
let code = generate(config).unwrap_or_else(|e| {
panic!("Failed to generate code from {xsd_path}: {e}");
});
let code = code.to_string();
let code = code
.replace(
"# [derive (Debug , Deserialize , Serialize)]",
"#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]",
)
.replace(
"# [derive (Debug , Default , Deserialize , Serialize)]",
"#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]",
);
let code = add_skip_serializing_if(&code);
if output_path.exists() {
if let Ok(existing) = fs::read_to_string(output_path) {
if existing == code {
return;
}
}
}
if let Some(parent) = output_path.parent() {
fs::create_dir_all(parent).unwrap();
}
let mut file = fs::File::create(output_path).unwrap();
file.write_all(code.as_bytes()).unwrap();
}
fn add_skip_serializing_if(code: &str) -> String {
let target = "# [serde (default , ";
let mut result = String::with_capacity(code.len() + 10000);
let mut remaining = code;
while let Some(pos) = remaining.find(target) {
result.push_str(&remaining[..pos]);
let after_target = &remaining[pos + target.len()..];
let boundary = after_target
.find("# [")
.or_else(|| after_target.find("pub struct "))
.or_else(|| after_target.find("pub enum "))
.or_else(|| after_target.find("pub type "))
.unwrap_or(after_target.len());
let field_slice = &after_target[..boundary];
if field_slice.contains(":: core :: option :: Option") {
result.push_str("# [serde (default , skip_serializing_if = \"Option::is_none\" , ");
} else if field_slice.contains(":: std :: vec :: Vec") {
result.push_str("# [serde (default , skip_serializing_if = \"Vec::is_empty\" , ");
} else {
result.push_str(target);
}
remaining = after_target;
}
result.push_str(remaining);
result
}