er7-redact 0.1.2

Redact patient detail from HL7 v2 messages in the ER7 pipe-hat encoding, without changing the shape of the message
Documentation
//! Three ways to say what to redact: build a policy in Rust, read one
//! from a file, or start from a built-in and edit it.
//!
//! Run with: `cargo run --example write_a_policy`

use er7_redact::{Action, Policy, Redactor};

fn main() -> Result<(), er7_redact::Error> {
    // 1. Built in Rust, rule by rule. Order matters: rules apply in the
    //    order they are listed.
    let built = Policy::new()
        .with("PID-3.1", Action::Pseudonym)?
        .with("PID-5", Action::redacted())?
        .with("PID-7", Action::First(4))?
        .with("PID-19", Action::Clear)?;

    // 2. Read from a policy file — the same thing, in the form a team
    //    reviews in a pull request.
    let read = Policy::parse(
        "
        PID-3.1  pseudonym    # keep linkage, lose the record number
        PID-5    replace REDACTED
        PID-7    first 4      # the birth year is enough for most tests
        PID-19   clear
        ",
    )?;
    assert_eq!(built, read);

    // 3. Start from a built-in and add to it. `--show-policy` on the
    //    command line writes the built-in out as a file to edit.
    let extended = Policy::patient_identifiers()
        .with("NTE-3", Action::Clear)? // free text: nothing positional finds what is in here
        .with("OBX-5", Action::Clear)?;
    assert_eq!(
        extended.rules.len(),
        Policy::patient_identifiers().rules.len() + 2
    );

    // A policy writes itself back out in the file format, so the one that
    // ran can be recorded beside the message it redacted.
    println!("{built}");
    assert_eq!(Policy::parse(&built.to_string())?, built);

    let text = "MSH|^~\\&|LAB\rPID|1||PATID1234||EVERYWOMAN^EVE||19610615|F";
    let mut message = er7::parse(text)?;
    Redactor::new(built).redact(&mut message);
    assert_eq!(message.query("PID-5.1")?.as_deref(), Some("REDACTED"));

    // A malformed policy is rejected at load time, with the line number:
    // a typo here means a value that silently was not redacted.
    let error = Policy::parse("PID-5 obfuscate").unwrap_err();
    println!("{error}");
    assert!(error.to_string().contains("policy line 1"));

    Ok(())
}