Skip to main content

Crate hl7_2_mllp

Crate hl7_2_mllp 

Source
Expand description

MLLP — the Minimal Lower Layer Protocol, which is how HL7 v2 messages actually cross a network.

A TCP stream is bytes without edges, and an HL7 v2 message carries no length prefix and no self-delimiting syntax, so a receiver reading a socket cannot tell where one message stops and the next begins. MLLP is the three-byte answer to that, and nothing more: wrap each message in a start block and an end block.

<VT> message <FS><CR>
0x0B          0x1C 0x0D

That is the whole protocol. It is deliberately minimal — no length, no checksum, no session, no negotiation, no encryption — and everything else people expect of a messaging layer is either HL7’s own acknowledgement messages (ack), TLS underneath, or the caller’s business.

§What is here

encode, decodeone frame in hand
Framera byte stream, where frames arrive split across reads or several to a read — this is the one a socket needs
Transport, IoTransportframes over anything that reads and writes bytes
ackturning a received message into the acknowledgement HL7 expects back
use hl7_2_mllp as mllp;

let message = "MSH|^~\\&|LAB|ACME|EHR|CLINIC|20260814080000||ORU^R01|99|P|2.5\rPID|1";
let frame = mllp::encode(message.as_bytes());

assert_eq!(frame[0], mllp::START_BLOCK);
assert_eq!(&frame[frame.len() - 2..], &[mllp::END_BLOCK, mllp::CARRIAGE_RETURN]);
assert_eq!(mllp::decode(&frame)?, message.as_bytes());

§Reading a socket

use hl7_2_mllp::{IoTransport, Transport};
use std::net::TcpListener;

let listener = TcpListener::bind("127.0.0.1:2575")?;
for stream in listener.incoming() {
    let mut transport = IoTransport::new(stream?);
    while let Some(message) = transport.receive()? {
        // ... process the message, then answer ...
    }
}

See examples/tcp_listener.rs for a complete server that also acknowledges, and examples/tcp_sender.rs for the other end.

§Strictness

By default this crate is strict: a frame must start with <VT>, end with <FS><CR>, and contain neither block character in between. Real senders are not always strict — a missing <CR> after <FS>, and stray bytes between frames, are the two common sins — so the noncompliance feature relaxes exactly those two and nothing else.

It is off by default because a receiver that quietly accepts malformed framing is how a truncated message becomes a clinical record. Turn it on when you have a specific sender that needs it, and know which of the two you are forgiving.

spec/index.md in the repository is the normative specification of everything above; where this documentation and that document disagree, that document is right.

Re-exports§

pub use ack::AckCode;
pub use hl7_2;

Modules§

ack
Answering a message.

Structs§

Framer
Turns a stream of bytes into whole MLLP frames.
IoTransport
A Transport over any byte stream: TcpStream, a TLS stream, a Unix socket, or a Cursor in a test.

Enums§

Error
What can go wrong reading a frame.
Tolerance
How much malformed framing to accept.

Constants§

CARRIAGE_RETURN
<CR>, the carriage return 0x0D that follows END_BLOCK.
DEFAULT_LIMIT
The default cap on how much a Framer buffers while waiting for an end block: 16 MiB.
END_BLOCK
<FS>, the end block: a file separator, 0x1C. Ends every frame, followed by CARRIAGE_RETURN.
START_BLOCK
<VT>, the start block: a vertical tab, 0x0B. Begins every frame.

Traits§

Transport
Sending and receiving whole MLLP frames.

Functions§

decode
Unwrap one complete frame, returning the payload.
decode_with
Unwrap one complete frame at a chosen Tolerance.
encode
Wrap a payload in a frame: <VT> + payload + <FS><CR>.
is_framable
Whether payload can be framed unambiguously — that is, whether it is free of the two bytes MLLP reserves.