Skip to main content

parse_with

Function parse_with 

Source
pub fn parse_with(
    input: &[u8],
    options: ParseOptions,
) -> Result<ParseOutcome, StepError>
Expand description

Parses a physical file under explicit ParseOptions.

With OnMalformed::Skip an unparsable data record is reported as a Diagnostic and the parser resynchronizes on the next record or section end, so a consumer can load a damaged file and still show exactly what was dropped. Header structure stays strict under every policy: mandatory header records are file-level invariants, not recoverable payload.

ยงErrors

Returns StepError for the wrong physical-file marker, for any header or section-structure defect, and for data defects when the policy is OnMalformed::Abort.

Examples found in repository?
examples/recovery_probe.rs (line 12)
3fn main() {
4    let path = std::env::args().nth(1).expect("path");
5    let bytes = std::fs::read(&path).expect("read");
6
7    match openbim_step::parse(&bytes) {
8        Ok(_) => println!("strict: OK (unexpected)"),
9        Err(error) => println!("strict: {error}"),
10    }
11
12    let outcome = openbim_step::parse_with(&bytes, openbim_step::ParseOptions::lenient())
13        .expect("lenient parse");
14    println!(
15        "lenient: {} records, {} diagnostics",
16        outcome.exchange.data.records.len(),
17        outcome.diagnostics.len()
18    );
19    for diagnostic in &outcome.diagnostics {
20        println!("  {diagnostic}");
21        let span = diagnostic.span();
22        println!(
23            "  dropped bytes: {:?}",
24            String::from_utf8_lossy(&bytes[span.start..span.end])
25        );
26    }
27    let boundaries = outcome
28        .exchange
29        .data
30        .records
31        .iter()
32        .filter(|record| {
33            record
34                .as_simple()
35                .is_some_and(|simple| simple.name == "IFCRELSPACEBOUNDARY")
36        })
37        .count();
38    println!("IfcRelSpaceBoundary: {boundaries}");
39    println!("schema: {:?}", outcome.exchange.header.standard().schema);
40}
More examples
Hide additional examples
examples/recovery_property.rs (lines 46-49)
18fn main() {
19    let base = "ISO-10303-21;\nHEADER;\nFILE_DESCRIPTION((''),'2;1');\n\
20                FILE_NAME('n','t',(''),(''),'p','o','a');\nFILE_SCHEMA(('IFC4'));\nENDSEC;\nDATA;\n\
21                #1= IFCPERSON($,$,'a;b',$,$,$,$,$);\n\
22                #2= IFCPIXELTEXTURE(1,1,8,(\"00AA;BB\"));\n\
23                #3= IFCORGANIZATION($,'o''x',$,$,$); /* c;c */\n\
24                #4= IFCDOOR('real');\nENDSEC;\nEND-ISO-10303-21;\n";
25
26    let mut state = 0x5eed_1234_u64;
27    let mut recovered = 0usize;
28    let mut fabricated = 0usize;
29    let mut span_faults = 0usize;
30
31    for _ in 0..20_000 {
32        let mut bytes = base.as_bytes().to_vec();
33        // Corrupt 1-3 bytes anywhere in the DATA region.
34        let edits = 1 + (lcg(&mut state) % 3) as usize;
35        for _ in 0..edits {
36            let at = 150 + (lcg(&mut state) as usize % (bytes.len() - 150));
37            match lcg(&mut state) % 3 {
38                0 => bytes[at] = b'@',
39                1 => {
40                    bytes.remove(at);
41                }
42                _ => bytes[at] = (lcg(&mut state) % 128) as u8,
43            }
44        }
45
46        let Ok(outcome) = parse_with(
47            &bytes,
48            ParseOptions::default().on_malformed_record(OnMalformed::Skip),
49        ) else {
50            continue;
51        };
52        recovered += 1;
53
54        for diagnostic in &outcome.diagnostics {
55            let span = diagnostic.span();
56            if span.start > span.end || span.end > bytes.len() {
57                span_faults += 1;
58            }
59        }
60
61        // Every kept record must appear verbatim in the surviving source: its
62        // `#id=` must exist outside any diagnostic-covered range.
63        for record in &outcome.exchange.data.records {
64            let needle = format!("#{}=", record.id.as_str());
65            let found = String::from_utf8_lossy(&bytes).contains(&needle)
66                || String::from_utf8_lossy(&bytes).contains(&format!("#{} =", record.id.as_str()));
67            if !found {
68                fabricated += 1;
69                eprintln!(
70                    "FABRICATED {} in:\n{}",
71                    needle,
72                    String::from_utf8_lossy(&bytes)
73                );
74            }
75        }
76    }
77
78    println!("recovered={recovered} fabricated={fabricated} span_faults={span_faults}");
79    assert_eq!(fabricated, 0, "recovery invented records");
80    assert_eq!(span_faults, 0, "diagnostic span out of bounds");
81    println!("PROPERTY PROBE OK");
82}