Skip to main content

Crate er7_redact

Crate er7_redact 

Source
Expand description

§ER7 redact

websitedocumentationsourcecrateemail

Remove patient detail from HL7 v2 messages in the ER7 pipe-hat encoding — without breaking the message.

A redacted message still parses, still declares the same delimiters, and still holds a value in every position that held one before, so everything downstream of it behaves the way it did on the original. That is the whole design: redaction rewrites leaf text and nothing else (D1).

Example:

use er7_redact::{Policy, Redactor};

let text = "MSH|^~\\&|LAB|ACME|EHR|CLINIC|20260815120000||ADT^A08|MSG9|P|2.5\r\
            PID|1||PATID1234^^^ACME^MR||EVERYWOMAN^EVE^E||19610615|F|||\
            12 ELM ST^^BOSTON^MA^02101|||555-555-1111";

let mut message = er7::parse(text)?;
let report = Redactor::new(Policy::patient_identifiers()).redact(&mut message);

// The name is a placeholder, the birth date is a year, the address and
// the phone number are gone, and the record number is a pseudonym.
assert_eq!(message.query("PID-5")?.as_deref(), Some("REDACTED^REDACTED^REDACTED"));
assert_eq!(message.query("PID-7")?.as_deref(), Some("1961"));
assert_eq!(message.query("PID-11")?.as_deref(), Some("^^^^"));
assert_eq!(message.query("PID-13")?.as_deref(), Some(""));
assert_ne!(message.query("PID-3.1")?.as_deref(), Some("PATID1234"));

// The shape did not move: the assigning authority is still there, in
// the component it was in, and the message still parses.
assert_eq!(message.query("PID-3.4")?.as_deref(), Some("ACME"));
assert!(er7::parse(&message.to_er7()).is_ok());

// And there is a record of exactly what changed.
assert_eq!(report.changes[0].path.to_string(), "PID[1]-3[1].1.1");

§Accept by default, or reject by default

Every policy is one of the two, and says which (Posture, spec §2.6):

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

// Accept by default — redact the positions the policy lists.
let listed = Policy::patient_identifiers();

// Reject by default — redact everything except what a `keep` rule names.
let all_but = Policy::all_but_the_header().with("OBX-5", Action::Keep)?;

Accepting by default is the safer message — it leaves the clinical content a test needs. Rejecting by default is the safer posture — it is the only one that covers a Z segment nobody documented, or a field an interface started sending last week. Where the two disagree inside one policy, the reject wins (D19).

§What is here

ItemPurpose
Redactora policy and a pseudonym key; the only thing that edits a message
Policy, Rule, Actionwhat to redact, where, and how
Postureaccept by default, or reject by default
Unrecognisedwhat a payload that is not ER7 gets
Policy::patient_identifiersthe curated default: PID, NK1, PV1, GT1, IN1
Policy::all_but_the_headerthe other posture, curated: redact all but what you name
Policy::accept_all, Policy::reject_allthe two bare postures, with no rules at all
Policy::parseread a policy file
Report, Changewhat a redaction did, with no values in it
pseudonym()the stable stand-in an identifier is replaced by

§What is deliberately not here

This crate is a positional editor, not a compliance tool. It does not know whether the positions it redacts are the ones your senders use; it cannot tell you whether what remains is de-identified, because that is a judgement about a whole data set made by a person who is accountable for it; and it cannot find an identifier written into free text, because no positional rule can.

There is also no way back. No mapping table, no key escrow, no undo.

A message this crate has redacted is a message with less in it, which is progress, and is not the same thing as a safe one.

§Documentation

spec/index.md in the repository is the normative specification of everything above; where this documentation and that document disagree, that document is right. Section references such as “spec §5.1” and rule IDs such as “D1” throughout these docs point into it.

The repository also holds a tutorial (docs/usage/), the policy reference (docs/policies/), an FAQ (docs/faq/), and runnable programs (examples/).

For the encoding layer underneath — parsing, queries, escape sequences, the absent/empty/null distinction — see the er7 crate, which is this crate’s only dependency.

Re-exports§

pub use crate::action::Action;
pub use crate::policy::Policy;
pub use crate::policy::Posture;
pub use crate::policy::Rule;
pub use crate::policy::Unrecognised;
pub use crate::pseudonym::pseudonym;
pub use crate::redact::Change;
pub use crate::redact::Redactor;
pub use crate::redact::Report;

Modules§

action
The eight things redaction can do to a value.
policy
Rules, policies, the four built-in policies, and the policy file format.
pseudonym
Stable stand-ins for identifiers.
redact
The engine: applying a policy to a message, and reporting what changed.

Enums§

Error
What can go wrong.