use fastxml::Parser;
use fastxml::error::Result;
use fastxml::event::XmlEvent;
use fastxml::schema::{Schema, Validator};
fn main() -> Result<()> {
let args: Vec<String> = std::env::args().collect();
if args.len() > 1 {
let file_path = &args[1];
return validate_file(file_path);
}
run_demo()
}
fn run_demo() -> Result<()> {
let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
<CityModel xmlns="http://www.opengis.net/citygml/2.0"
xmlns:bldg="http://www.opengis.net/citygml/building/2.0"
xmlns:gml="http://www.opengis.net/gml">
<cityObjectMember>
<bldg:Building gml:id="BLDG_001">
<bldg:measuredHeight uom="m">25.5</bldg:measuredHeight>
<bldg:storeysAboveGround>8</bldg:storeysAboveGround>
<bldg:yearOfConstruction>1995</bldg:yearOfConstruction>
</bldg:Building>
</cityObjectMember>
<cityObjectMember>
<bldg:Building gml:id="BLDG_002">
<bldg:measuredHeight uom="m">32.0</bldg:measuredHeight>
<bldg:storeysAboveGround>10</bldg:storeysAboveGround>
<bldg:yearOfConstruction>2005</bldg:yearOfConstruction>
</bldg:Building>
</cityObjectMember>
</CityModel>
"#;
println!("=== Streaming Validation Example (Demo) ===\n");
println!("Starting streaming parse with validation...\n");
let report = Validator::from(xml).schema(Schema::builtin()).run()?;
let mut element_count = 0usize;
Parser::from(xml).for_each_event(|event| {
if matches!(event, XmlEvent::StartElement { .. }) {
element_count += 1;
}
Ok(())
})?;
println!("\n=== Results ===\n");
println!("Total elements processed: {element_count}");
println!(
"Validation: {} (using built-in schema)",
if report.is_valid() {
"PASSED"
} else {
"FAILED"
}
);
println!("\nStreaming validation complete!");
println!("Note: Memory usage stays constant regardless of file size.");
Ok(())
}
#[cfg(feature = "ureq")]
fn validate_file(file_path: &str) -> Result<()> {
use fastxml::schema::DefaultFetcher;
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
println!("=== Streaming Validation: {} ===\n", file_path);
let file = File::open(file_path).map_err(fastxml::error::Error::Io)?;
let file_size = file.metadata().map(|m| m.len()).unwrap_or(0);
println!("File size: {:.2} MB", file_size as f64 / 1024.0 / 1024.0);
println!("Starting streaming parse with validation...");
println!("(Schema will be fetched from xsi:schemaLocation if present)\n");
let start = std::time::Instant::now();
let reader = BufReader::new(file);
let base_dir = Path::new(file_path)
.parent()
.map(|p| p.to_path_buf())
.unwrap_or_default();
let fetcher = DefaultFetcher::with_base_dir(base_dir);
let report = Validator::from_reader(reader).run_with(fetcher)?;
let elapsed = start.elapsed();
for err in report.entries() {
if err.is_error() {
println!("[ERROR] {}", err.message);
} else {
println!("[WARN] {}", err.message);
}
}
println!("\n=== Results ===\n");
println!("Time: {:.2?}", elapsed);
println!(
"Throughput: {:.2} MB/s",
file_size as f64 / 1024.0 / 1024.0 / elapsed.as_secs_f64()
);
println!(
"Errors: {}, Warnings: {}",
report.error_count(),
report.warning_count()
);
if report.is_valid() {
println!("\nValidation: PASSED");
} else {
println!("\nValidation: FAILED");
}
Ok(())
}
#[cfg(not(feature = "ureq"))]
fn validate_file(file_path: &str) -> Result<()> {
eprintln!("Error: File validation requires the 'ureq' feature for schema fetching.");
eprintln!(
"Run with: cargo run --example streaming_validation --features ureq -- {}",
file_path
);
std::process::exit(1);
}