Skip to main content

haste_hl7v2/
mllp.rs

1use haste_fhir_model::r4::generated::terminology::IssueType;
2use haste_fhir_operation_error::OperationOutcomeError;
3use std::io::Read;
4
5const START_BLOCK: u8 = 0x0B;
6const END_BLOCK: u8 = 0x1C;
7const CARRIAGE_RETURN: u8 = 0x0D;
8const COMMIT_ACK: u8 = 0x06;
9const COMMIT_NAK: u8 = 0x15;
10
11pub struct MllpFormatter;
12
13impl MllpFormatter {
14    pub fn encode(payload: &[u8]) -> Vec<u8> {
15        let mut buf = Vec::with_capacity(payload.len() + 3);
16        buf.push(START_BLOCK);
17        buf.extend_from_slice(payload);
18        buf.push(END_BLOCK);
19        buf.push(CARRIAGE_RETURN);
20        buf
21    }
22
23    pub fn decode(framed: &[u8]) -> Result<&[u8], OperationOutcomeError> {
24        if framed.len() < 4
25            || framed[0] != START_BLOCK
26            || framed[framed.len() - 2] != END_BLOCK
27            || framed[framed.len() - 1] != CARRIAGE_RETURN
28        {
29            let k = String::from_utf8_lossy(framed);
30            return Err(OperationOutcomeError::error(
31                IssueType::Exception(None),
32                format!("Expected MLLP frame <SB>...<EB><CR>, got: {:?}", k),
33            ));
34        }
35        Ok(&framed[1..framed.len() - 2])
36    }
37
38    pub fn ack() -> [u8; 4] {
39        [START_BLOCK, COMMIT_ACK, END_BLOCK, CARRIAGE_RETURN]
40    }
41
42    pub fn nak() -> [u8; 4] {
43        [START_BLOCK, COMMIT_NAK, END_BLOCK, CARRIAGE_RETURN]
44    }
45
46    pub fn is_ack(bytes: &[u8]) -> bool {
47        bytes == Self::ack()
48    }
49
50    pub fn is_nak(bytes: &[u8]) -> bool {
51        bytes == Self::nak()
52    }
53
54    /// Reads a single MLLP frame from `reader`, returning the raw framed bytes
55    /// (including the START_BLOCK / END_BLOCK / CARRIAGE_RETURN wrappers).
56    /// Returns an error on EOF mid-frame or an I/O error.
57    pub fn read_frame<R: Read>(reader: &mut R) -> Result<Vec<u8>, OperationOutcomeError> {
58        let mut buf = Vec::new();
59        let mut byte = [0u8; 1];
60        loop {
61            match reader.read(&mut byte) {
62                Ok(0) => {
63                    return Err(OperationOutcomeError::error(
64                        IssueType::Exception(None),
65                        "Connection closed before complete MLLP frame".to_string(),
66                    ));
67                }
68                Ok(_) => {
69                    buf.push(byte[0]);
70                    let len = buf.len();
71
72                    if buf[0] != START_BLOCK {
73                        return Err(OperationOutcomeError::error(
74                            IssueType::Exception(None),
75                            "MLLP frame does not start with START_BLOCK".to_string(),
76                        ));
77                    }
78
79                    if len >= 2 && buf[len - 2] == END_BLOCK && buf[len - 1] == CARRIAGE_RETURN {
80                        return Ok(buf);
81                    }
82                }
83                Err(e) => {
84                    return Err(OperationOutcomeError::error(
85                        IssueType::Exception(None),
86                        format!("Failed to read from stream: {}", e),
87                    ));
88                }
89            }
90        }
91    }
92}