Skip to main content

Message

Struct Message 

Source
pub struct Message(pub Message);
Expand description

A Serde-enabled er7::Message — the crate’s main entry point.

This is the type most callers reach for: parse ER7 with Message::parse (or wrap an er7::Message you already have), hand it to any Serializerserde_json::to_string, a YAML or CBOR writer, anything Serde-compatible — and get it back the same way on the other end.

It serializes as an object with two fields, "separators" and "segments", exactly the shape er7::Message itself has (er7::Message::separators, er7::Message::segments) and the shape serde’s own manual-implementation guide walks through for a struct with named fields.

§What round-trips and what does not

Every subcomponent serializes as its raw text — escape sequences intact, not decoded — so Message::parse(text)? through any Serde format and back out through er7::Message::to_er7 reproduces the original bytes wherever er7::parse(text)?.to_er7() already would (see er7::Message::to_er7 for exactly when that is: canonical input round-trips unchanged; non-canonical terminators and blank lines are normalized once, at the first parse, same as in plain er7).

Example:

use serde_er7::Message;

let text = "MSH|^~\\&|LAB|ACME|EHR|CLINIC|20260815120000||ORU^R01|MSG9|P|2.5\r\
            PID|1||12345^^^ACME^MR||SMITH^JOHN^Q||19800101|M\r\
            OBX|1|NM|2093-3^Cholesterol^LN||187|mg/dL|||||F";

let message = Message::parse(text)?;

// Any Serde format works; this crate never mentions JSON itself.
let json = serde_json::to_string_pretty(&message)?;
assert!(json.contains(r#""name": "PID""#));

// ...and it comes back the same message.
let back: Message = serde_json::from_str(&json)?;
assert_eq!(back.to_er7(), text);

Tuple Fields§

§0: Message

Implementations§

Source§

impl Message

Source

pub fn parse(text: &str) -> Result<Message, Error>

Parse ER7 text directly into a Serde-enabled Message.

A thin wrapper over er7::parse(), so the crate’s flagship path — text in, any Serde format out — needs only this one call plus whichever format’s to_string/to_writer.

Example:

use serde_er7::Message;

let message = Message::parse("MSH|^~\\&|LAB\rPID|1")?;
assert_eq!(message.segments.len(), 2);
§Errors

Returns er7::Error exactly as er7::parse() does: the input held no segments, the first segment is not a header, or the header declared an unusable delimiter set. Nothing is added here.

Methods from Deref<Target = Message>§

Source

pub fn segments_named<'a>( &'a self, name: &'a str, ) -> impl Iterator<Item = &'a Segment>

Every segment with this name, in message order.

Example:

let message = er7::parse("MSH|^~\\&|LAB\rOBX|1\rOBX|2\rNTE|1")?;

assert_eq!(message.segments_named("OBX").count(), 2);
assert_eq!(message.segments_named("ZZZ").count(), 0);
Source

pub fn segment(&self, name: &str) -> Option<&Segment>

The first segment with this name.

Example:

let message = er7::parse("MSH|^~\\&|LAB\rPID|1\rOBX|1\rOBX|2")?;

assert_eq!(message.segment("OBX").unwrap().field(1).unwrap()
           .to_er7(&message.separators), "1");
assert!(message.segment("ZZZ").is_none());
Source

pub fn segment_at(&self, name: &str, occurrence: usize) -> Option<&Segment>

The 1-based occurrenceth segment with this name, e.g. the second OBX of a result.

Example:

let message = er7::parse("MSH|^~\\&|LAB\rOBX|1\rOBX|2")?;

let second = message.segment_at("OBX", 2).unwrap();
assert_eq!(second.field(1).unwrap().to_er7(&message.separators), "2");
assert!(message.segment_at("OBX", 3).is_none());
Source

pub fn segment_at_mut( &mut self, name: &str, occurrence: usize, ) -> Option<&mut Segment>

Mutable access to the 1-based occurrenceth segment with this name.

Source

pub fn header(&self) -> Option<&Segment>

The header segment — the first segment, which declared the delimiters. A parsed message always has one; a message built by hand might not, hence the Option.

Example:

let message = er7::parse("MSH|^~\\&|LAB\rPID|1")?;
assert_eq!(message.header().unwrap().name, "MSH");

// A batch envelope header works the same way.
let batch = er7::parse("BHS|^~\\&|SENDER")?;
assert_eq!(batch.header().unwrap().name, "BHS");
Source

pub fn query(&self, path: &str) -> Result<Option<String>, Error>

The decoded text at path, or None if the message has nothing there.

A path that names a level above a subcomponent returns that whole subtree written back as ER7, with only the leaf text decoded — so PID-5 on SMITH^JOHN gives SMITH^JOHN and PID-5.1 gives SMITH. Where the path leaves an occurrence open, the first is taken; use Message::query_all to get them all.

A position the message does not carry gives None rather than an error or an empty string (R20, spec §8.2). The Err case is only a malformed path.

Example:

let message = er7::parse("MSH|^~\\&|LAB\rPID|1||9|4|SMITH^JOHN")?;

assert_eq!(message.query("PID-5")?.as_deref(), Some("SMITH^JOHN"));
assert_eq!(message.query("PID-5.2")?.as_deref(), Some("JOHN"));
assert_eq!(message.query("PID-99")?, None);
assert_eq!(message.query("ZZZ-1")?, None);
assert!(message.query("PID-0").is_err());
§Errors

Error::BadPath only — and only for a malformed path. A position the message does not carry is Ok(None), never an error (R20).

Source

pub fn query_all(&self, path: &str) -> Result<Vec<String>, Error>

Every value matching path, in message order: one per matching segment, and one per repetition where the path does not pin one down. Repeated OBX-5 across a result is the motivating case.

Example:

let message = er7::parse(
    "MSH|^~\\&|LAB\r\
     PID|555-1111~555-2222\r\
     OBX|1|NM|2093-3^Cholesterol^LN||187\r\
     OBX|2|NM|2571-8^Triglycerides^LN||102\r\
     OBX|3|ST|X^Note^L",
)?;

// One value per matching segment.
assert_eq!(message.query_all("OBX-3.2")?, ["Cholesterol", "Triglycerides", "Note"]);
// The third OBX carried no fifth field, so it contributes nothing.
assert_eq!(message.query_all("OBX-5")?, ["187", "102"]);
// Stopping at the field keeps the repetition separator...
assert_eq!(message.query_all("PID-1")?, ["555-1111~555-2222"]);
// ...going deeper splits it.
assert_eq!(message.query_all("PID-1.1")?, ["555-1111", "555-2222"]);
§Errors

Error::BadPath only; see Message::query. A path that matches nothing gives an empty Vec.

Source

pub fn query_path(&self, path: &Path) -> Vec<String>

Message::query_all against an already-parsed Path, which saves re-parsing when the same path is applied to many messages.

Example:

let path: er7::Path = "PID-5.1".parse()?;
let messages = [
    er7::parse("MSH|^~\\&|LAB\rPID|1||9|4|SMITH^JOHN")?,
    er7::parse("MSH|^~\\&|LAB\rPID|1||9|4|JONES^MARY")?,
];

let names: Vec<String> = messages
    .iter()
    .flat_map(|message| message.query_path(&path))
    .collect();
assert_eq!(names, ["SMITH", "JONES"]);
Source

pub fn query_path_raw(&self, path: &Path) -> Vec<String>

Message::query_path without decoding: every value comes back exactly as the sender wrote it, escape sequences included.

Use this when you are going to put the text back into a message rather than read it, or when you need to tell an explicit null from an empty value without reaching for the node (spec §8.2.1).

Example:

let message = er7::parse("MSH|^~\\&|LAB\rPID|Smith \\T\\ Jones|\"\"|")?;

let name: er7::Path = "PID-1".parse()?;
assert_eq!(message.query_path(&name), ["Smith & Jones"]);
assert_eq!(message.query_path_raw(&name), [r"Smith \T\ Jones"]);

// Decoded, a null and an empty field look the same; raw, they do not.
let null: er7::Path = "PID-2".parse()?;
let empty: er7::Path = "PID-3".parse()?;
assert_eq!(message.query_path(&null), message.query_path(&empty));
assert_eq!(message.query_path_raw(&null), [r#""""#]);
assert_eq!(message.query_path_raw(&empty), [""]);
Source

pub fn message_code(&self) -> Option<String>

MSH-9.1, the message code, e.g. ADT.

This and the four accessors below are the only HL7 semantics this crate knows, and they are the documented exception to R24. They earn their place on two grounds that both have to hold: every tool that touches a message needs them to route or log it, and these positions have not moved in any v2 release, so reading them requires no version knowledge (spec §10.2).

Each returns None when the position is absent or empty, because for these five “sent blank” and “not sent” mean the same thing to a caller (R22).

Example:

let message = er7::parse(
    "MSH|^~\\&|LAB|ACME|EHR|CLINIC|20260815120000||ADT^A08^ADT_A01|MSG9|P|2.5",
)?;

assert_eq!(message.message_code().as_deref(), Some("ADT"));
assert_eq!(message.trigger_event().as_deref(), Some("A08"));
assert_eq!(message.message_structure().as_deref(), Some("ADT_A01"));
assert_eq!(message.control_id().as_deref(), Some("MSG9"));
assert_eq!(message.version().as_deref(), Some("2.5"));

// Absent or empty both read as None.
let sparse = er7::parse("MSH|^~\\&|LAB")?;
assert_eq!(sparse.message_code(), None);
assert_eq!(sparse.control_id(), None);
Source

pub fn trigger_event(&self) -> Option<String>

MSH-9.2, the trigger event, e.g. A08 — what happened that caused the message. See Message::message_code for an example.

Source

pub fn message_structure(&self) -> Option<String>

MSH-9.3, the message structure, e.g. ADT_A01 — which segments the message may hold.

Older senders often omit it, in which case the structure has to be derived from the code and trigger event. This crate deliberately does not do that: the mapping differs between HL7 versions, and a wrong answer routes a message to the wrong handler (spec §10.3). Derive it in the dictionary layer that knows the version.

See Message::message_code for an example.

Source

pub fn control_id(&self) -> Option<String>

MSH-10, the message control ID: the sender’s unique identifier for this message, and what an acknowledgement quotes back in MSA-2.

See Message::message_code for an example.

Source

pub fn version(&self) -> Option<String>

MSH-12.1, the HL7 version ID, e.g. 2.5.

This reads the first component rather than the whole field, because a v2.5.1-and-later sender may write 2.5.1^AUS^2.5.1 and only the first component is the version ID (spec §10.1).

See Message::message_code for an example.

Source

pub fn to_er7(&self) -> String

Write the message as ER7 with the default RenderOptions: carriage-return terminators and no trailing terminator.

Parsing and writing round-trip: for any message this crate parsed, the output differs from the input only where the input was not already canonical — that is, where it had blank lines or a different terminator, the two things parsing normalizes (R16, spec §7.2).

Example:

// Canonical input comes back byte for byte.
let text = "MSH|^~\\&|LAB\rPID|1||9|4|SMITH^JOHN";
assert_eq!(er7::parse(text)?.to_er7(), text);

// Including unusual delimiters, empty positions, and escapes the
// crate does not decode.
for text in [
    "MSH#*!?@#LAB#*A*B#C!D",
    "MSH|^~\\&|LAB\rPID||A~~B|^^C|||D",
    "MSH|^~\\&|LAB\rNTE|1||line\\.br\\next\rZPD|1|LOCAL",
] {
    assert_eq!(er7::parse(text)?.to_er7(), text);
}

// Non-canonical input is normalized, and then round-trips.
let messy = "MSH|^~\\&|LAB\r\n\r\nPID|1\n";
assert_eq!(er7::parse(messy)?.to_er7(), "MSH|^~\\&|LAB\rPID|1");
Source

pub fn to_er7_with(&self, options: RenderOptions) -> String

Write the message as ER7, choosing the segment terminator and whether the last segment gets one.

Example:

use er7::{RenderOptions, Terminator};

let message = er7::parse("MSH|^~\\&|LAB\rPID|1")?;

// Readable in a terminal.
let readable = RenderOptions { terminator: Terminator::Lf, ..Default::default() };
assert_eq!(message.to_er7_with(readable), "MSH|^~\\&|LAB\nPID|1");

// Strict wire output terminates every segment, the last included.
let wire = RenderOptions { terminator: Terminator::Cr, trailing_terminator: true };
assert_eq!(message.to_er7_with(wire), "MSH|^~\\&|LAB\rPID|1\r");

Trait Implementations§

Source§

impl Clone for Message

Source§

fn clone(&self) -> Message

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Message

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Deref for Message

Source§

type Target = Message

The resulting type after dereferencing.
Source§

fn deref(&self) -> &Message

Dereferences the value.
Source§

impl DerefMut for Message

Source§

fn deref_mut(&mut self) -> &mut Message

Mutably dereferences the value.
Source§

impl<'de> Deserialize<'de> for Message

Source§

fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Display for Message

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

The message as ER7; see er7::Message::to_er7.

Source§

impl Eq for Message

Source§

impl From<Message> for Message

Source§

fn from(inner: Message) -> Message

Converts to this type from the input type.
Source§

impl From<Message> for Message

Source§

fn from(outer: Message) -> Message

Converts to this type from the input type.
Source§

impl PartialEq for Message

Source§

fn eq(&self, other: &Message) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for Message

Source§

fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for Message

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<P, T> Receiver for P
where P: Deref<Target = T> + ?Sized, T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.