use ooxml_codegen::{
CodegenConfig, FeatureMappings, NameMappings, Schema, analyze_schema, generate,
generate_parsers, generate_serializers, parse_rnc,
};
use std::fs;
use std::path::Path;
fn main() {
let spec_dir = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../spec/OfficeOpenXML-RELAXNG-Transitional"
);
let names_path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../spec/ooxml-names.yaml");
let features_path = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../spec/ooxml-features.yaml"
);
let dml_path = format!("{}/dml-main.rnc", spec_dir);
let chart_path = format!("{}/dml-chart.rnc", spec_dir);
let diagram_path = format!("{}/dml-diagram.rnc", spec_dir);
let shared_path = format!("{}/shared-commonSimpleTypes.rnc", spec_dir);
let rel_path = format!("{}/shared-relationshipReference.rnc", spec_dir);
println!("cargo::rerun-if-changed={}", dml_path);
println!("cargo::rerun-if-changed={}", chart_path);
println!("cargo::rerun-if-changed={}", diagram_path);
println!("cargo::rerun-if-changed={}", shared_path);
println!("cargo::rerun-if-changed={}", rel_path);
println!("cargo::rerun-if-changed={}", names_path);
println!("cargo::rerun-if-changed={}", features_path);
println!("cargo::rerun-if-changed=build.rs");
let should_regenerate = std::env::var("OOXML_REGENERATE").is_ok();
let dest_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("src/generated.rs");
if !should_regenerate {
return;
}
if !Path::new(&dml_path).exists() {
eprintln!(
"Warning: Schema not found at {}. Run scripts/download-spec.sh first.",
dml_path
);
return;
}
eprintln!("Regenerating src/generated.rs from schemas...");
let mut combined_schema = if Path::new(&shared_path).exists() {
let shared_input = fs::read_to_string(&shared_path).expect("failed to read shared types");
parse_rnc(&shared_input).expect("failed to parse shared types")
} else {
Schema {
namespaces: vec![],
definitions: vec![],
}
};
if Path::new(&rel_path).exists() {
let rel_input =
fs::read_to_string(&rel_path).expect("failed to read relationship references");
let rel_schema = parse_rnc(&rel_input).expect("failed to parse relationship references");
for ns in rel_schema.namespaces {
if !combined_schema
.namespaces
.iter()
.any(|n| n.prefix == ns.prefix)
{
combined_schema.namespaces.push(ns);
}
}
combined_schema.definitions.extend(rel_schema.definitions);
}
let dml_input = fs::read_to_string(&dml_path).expect("failed to read dml-main.rnc");
let dml_schema = parse_rnc(&dml_input).expect("failed to parse dml-main.rnc");
for ns in dml_schema.namespaces {
if !combined_schema
.namespaces
.iter()
.any(|n| n.prefix == ns.prefix)
{
combined_schema.namespaces.push(ns);
}
}
combined_schema.definitions.extend(dml_schema.definitions);
if Path::new(&chart_path).exists() {
let chart_input = fs::read_to_string(&chart_path).expect("failed to read dml-chart.rnc");
let chart_schema = parse_rnc(&chart_input).expect("failed to parse dml-chart.rnc");
for ns in chart_schema.namespaces {
if !combined_schema
.namespaces
.iter()
.any(|n| n.prefix == ns.prefix)
{
combined_schema.namespaces.push(ns);
}
}
combined_schema.definitions.extend(chart_schema.definitions);
}
if Path::new(&diagram_path).exists() {
let diagram_input =
fs::read_to_string(&diagram_path).expect("failed to read dml-diagram.rnc");
let diagram_schema = parse_rnc(&diagram_input).expect("failed to parse dml-diagram.rnc");
for ns in diagram_schema.namespaces {
if !combined_schema
.namespaces
.iter()
.any(|n| n.prefix == ns.prefix)
{
combined_schema.namespaces.push(ns);
}
}
combined_schema
.definitions
.extend(diagram_schema.definitions);
}
let name_mappings = if Path::new(names_path).exists() {
match NameMappings::from_yaml_file(Path::new(names_path)) {
Ok(mappings) => {
eprintln!("Loaded name mappings from {}", names_path);
Some(mappings)
}
Err(e) => {
eprintln!("Warning: Failed to load name mappings: {}", e);
None
}
}
} else {
None
};
let feature_mappings = if Path::new(features_path).exists() {
match FeatureMappings::from_yaml_file(Path::new(features_path)) {
Ok(mappings) => {
eprintln!("Loaded feature mappings from {}", features_path);
Some(mappings)
}
Err(e) => {
eprintln!("Warning: Failed to load feature mappings: {}", e);
None
}
}
} else {
None
};
let config = CodegenConfig {
strip_prefix: Some("a_".to_string()),
module_name: "dml".to_string(),
name_mappings,
feature_mappings,
xml_serialize_prefix: Some("a".to_string()),
warn_unmapped: true,
..Default::default()
};
if std::env::var("OOXML_ANALYZE").is_ok() {
eprintln!("\n=== Static Analysis: DML ===");
let report = analyze_schema(&combined_schema, &config);
report.print("dml");
eprintln!();
}
let code = generate(&combined_schema, &config);
fs::write(&dest_path, code).expect("failed to write generated types");
eprintln!(
"Generated {} bytes to src/generated.rs",
dest_path.metadata().map(|m| m.len()).unwrap_or(0)
);
if std::env::var("OOXML_GENERATE_PARSERS").is_ok() {
let parser_dest = Path::new(env!("CARGO_MANIFEST_DIR")).join("src/generated_parsers.rs");
let parser_code = generate_parsers(&combined_schema, &config);
fs::write(&parser_dest, parser_code).expect("failed to write generated parsers");
eprintln!(
"Generated {} bytes to src/generated_parsers.rs",
parser_dest.metadata().map(|m| m.len()).unwrap_or(0)
);
}
if std::env::var("OOXML_GENERATE_SERIALIZERS").is_ok() {
let serializer_dest =
Path::new(env!("CARGO_MANIFEST_DIR")).join("src/generated_serializers.rs");
let serializer_code = generate_serializers(&combined_schema, &config);
fs::write(&serializer_dest, serializer_code)
.expect("failed to write generated serializers");
eprintln!(
"Generated {} bytes to src/generated_serializers.rs",
serializer_dest.metadata().map(|m| m.len()).unwrap_or(0)
);
}
}