use std::env;
use std::fs;
use std::path::PathBuf;
use ifc_schema::artifact_encode_schema as encode_schema;
struct Target {
selector: &'static str,
output: &'static str,
entities: usize,
types: usize,
label: &'static str,
}
const TARGETS: &[Target] = &[
Target {
selector: "ifc2x3",
output: "data/ifc2x3-tc1.bin",
entities: 653,
types: 327,
label: "IFC2x3 TC1",
},
Target {
selector: "ifc4",
output: "data/ifc4-add2-tc1.bin",
entities: 776,
types: 397,
label: "IFC4 ADD2 TC1",
},
Target {
selector: "ifc4x3",
output: "data/ifc4x3-add2.bin",
entities: 876,
types: 436,
label: "IFC4X3 ADD2",
},
];
fn main() {
if let Err(error) = run() {
eprintln!("ifc-schema-generate: {error}");
std::process::exit(1);
}
}
fn run() -> Result<(), String> {
let mut arguments = env::args_os().skip(1);
let selector = arguments
.next()
.and_then(|value| value.into_string().ok())
.ok_or_else(usage)?;
let source = arguments.next().map(PathBuf::from).ok_or_else(usage)?;
if arguments.next().is_some() {
return Err(usage());
}
let target = TARGETS
.iter()
.find(|target| target.selector.eq_ignore_ascii_case(&selector))
.ok_or_else(|| format!("unknown schema `{selector}`\n{}", usage()))?;
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let output = manifest_dir.join(target.output);
let bytes = fs::read(&source).map_err(|error| format!("read {}: {error}", source.display()))?;
let text: String = bytes.iter().map(|&b| b as char).collect();
let mut parsed = openbim_step::express::parse(&text);
for entity in &mut parsed.entities {
for rule in &mut entity.where_rules {
rule.expression.clear();
}
}
let entity_count = parsed.entities.len();
let type_count = parsed.types.len();
if entity_count != target.entities {
return Err(format!(
"{} declares {} entities, got {entity_count} -- wrong source file?",
target.label, target.entities
));
}
if type_count != target.types {
return Err(format!(
"{} declares {} types, got {type_count} -- wrong source file?",
target.label, target.types
));
}
let encoded = encode_schema(&parsed).map_err(|error| format!("encode artifact: {error}"))?;
let decoded = ifc_schema::artifact_decode_schema(&encoded)
.map_err(|error| format!("verify artifact: {error}"))?;
if decoded != parsed {
return Err("decoded artifact does not match the parsed schema".into());
}
let temporary = output.with_extension("bin.tmp");
fs::write(&temporary, &encoded)
.map_err(|error| format!("write {}: {error}", temporary.display()))?;
fs::rename(&temporary, &output)
.map_err(|error| format!("replace {}: {error}", output.display()))?;
println!(
"wrote {} bytes, {entity_count} entities, {type_count} types to {}",
encoded.len(),
output.display()
);
Ok(())
}
fn usage() -> String {
let mut text = String::from("usage: ifc-schema-generate <schema> <source.exp>\nschemas:\n");
for target in TARGETS {
text.push_str(&format!(
" {:<8} {} -> {}\n",
target.selector, target.label, target.output
));
}
text
}