Skip to main content

reject_by_default/
reject_by_default.rs

1//! The other posture: reject every value by default, and name what to
2//! accept.
3//!
4//! Use this when the message is unfamiliar — full of `Z` segments nobody
5//! documented, say — and the answer to "is there anything else in here?"
6//! has to be "no" rather than "not that I listed".
7//!
8//! Run with: `cargo run --example reject_by_default`
9
10use er7_redact::{Action, Policy, Redactor};
11
12fn main() -> Result<(), er7_redact::Error> {
13    let text = "MSH|^~\\&|LAB|ACME|EHR|CLINIC|20260815120000||ORU^R01|MSG9|P|2.5\r\
14                PID|1||PATID1234||EVERYWOMAN^EVE||19610615|F\r\
15                OBX|1|NM|2093-3^Cholesterol^LN||187|mg/dL|100-199|H|||F\r\
16                ZPD|1|LOCAL^EXTENSION^SEGMENT";
17
18    // Rejecting by default covers every leaf no rule named; a `keep` rule
19    // accepts a position, exempting it from that. A rejecting rule would
20    // beat the `keep` whichever order the two were written in (D19).
21    let policy = Policy::all_but_the_header()
22        .with("OBX-2", Action::Keep)? // value type
23        .with("OBX-3", Action::Keep)? // observation identifier
24        .with("OBX-5", Action::Keep)? // the number the test asserts on
25        .with("OBX-6", Action::Keep)?; // units
26
27    let mut message = er7::parse(text)?;
28    let report = Redactor::new(policy).redact(&mut message);
29    println!("{}\n", message.to_er7().replace('\r', "\n"));
30
31    // The header is untouched, so the message still routes and still says
32    // which version it is.
33    assert_eq!(message.query("MSH-9")?.as_deref(), Some("ORU^R01"));
34    assert_eq!(message.version().as_deref(), Some("2.5"));
35
36    // The result is intact, because four rules said so.
37    assert_eq!(message.query("OBX-3.2")?.as_deref(), Some("Cholesterol"));
38    assert_eq!(message.query("OBX-5")?.as_deref(), Some("187"));
39
40    // Everything else is gone — including the local segment, which no
41    // curated policy could have known about.
42    assert_eq!(message.query("PID-5.1")?.as_deref(), Some("REDACTED"));
43    assert_eq!(message.query("ZPD-2.1")?.as_deref(), Some("REDACTED"));
44
45    // The cost: values a positional policy would have kept are gone too.
46    assert_eq!(message.query("PID-8")?.as_deref(), Some("REDACTED"));
47    assert_eq!(message.query("OBX-8")?.as_deref(), Some("REDACTED"));
48
49    println!("{} positions changed", report.len());
50
51    // And what this posture is really for: a payload that is not ER7 at
52    // all has no positions to name, so the policy says outright what
53    // becomes of it. The curated policy above refuses one, which is what
54    // makes the CLI exit non-zero rather than write it out.
55    let redactor = Redactor::new(Policy::all_but_the_header());
56    assert_eq!(redactor.unrecognised("{\"name\": \"EVERYWOMAN\"}"), None);
57
58    // `Policy::reject_all` masks it whole instead — nothing routable
59    // survives, and neither does anything else.
60    let redactor = Redactor::new(Policy::reject_all());
61    assert_eq!(
62        redactor.unrecognised("EVERYWOMAN").as_deref(),
63        Some("**********")
64    );
65    Ok(())
66}