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 #[must_use]
15 pub fn encode(payload: &[u8]) -> Vec<u8> {
16 let mut buf = Vec::with_capacity(payload.len() + 3);
17 buf.push(START_BLOCK);
18 buf.extend_from_slice(payload);
19 buf.push(END_BLOCK);
20 buf.push(CARRIAGE_RETURN);
21 buf
22 }
23
24 pub fn decode(framed: &[u8]) -> Result<&[u8], OperationOutcomeError> {
32 if framed.len() < 4
33 || framed[0] != START_BLOCK
34 || framed[framed.len() - 2] != END_BLOCK
35 || framed[framed.len() - 1] != CARRIAGE_RETURN
36 {
37 let k = String::from_utf8_lossy(framed);
38 return Err(OperationOutcomeError::error(
39 IssueType::exception(),
40 format!("Expected MLLP frame <SB>...<EB><CR>, got: {k:?}"),
41 ));
42 }
43 Ok(&framed[1..framed.len() - 2])
44 }
45
46 #[must_use]
47 pub fn ack() -> [u8; 4] {
48 [START_BLOCK, COMMIT_ACK, END_BLOCK, CARRIAGE_RETURN]
49 }
50
51 #[must_use]
52 pub fn nak() -> [u8; 4] {
53 [START_BLOCK, COMMIT_NAK, END_BLOCK, CARRIAGE_RETURN]
54 }
55
56 #[must_use]
57 pub fn is_ack(bytes: &[u8]) -> bool {
58 bytes == Self::ack()
59 }
60
61 #[must_use]
62 pub fn is_nak(bytes: &[u8]) -> bool {
63 bytes == Self::nak()
64 }
65
66 pub fn read_frame<R: Read>(reader: &mut R) -> Result<Vec<u8>, OperationOutcomeError> {
77 let mut buf = Vec::new();
78 let mut byte = [0u8; 1];
79 loop {
80 match reader.read(&mut byte) {
81 Ok(0) => {
82 return Err(OperationOutcomeError::error(
83 IssueType::exception(),
84 "Connection closed before complete MLLP frame".to_string(),
85 ));
86 }
87 Ok(_) => {
88 buf.push(byte[0]);
89 let len = buf.len();
90
91 if buf[0] != START_BLOCK {
92 return Err(OperationOutcomeError::error(
93 IssueType::exception(),
94 "MLLP frame does not start with START_BLOCK".to_string(),
95 ));
96 }
97
98 if len >= 2 && buf[len - 2] == END_BLOCK && buf[len - 1] == CARRIAGE_RETURN {
99 return Ok(buf);
100 }
101 }
102 Err(e) => {
103 return Err(OperationOutcomeError::error(
104 IssueType::exception(),
105 format!("Failed to read from stream: {e}"),
106 ));
107 }
108 }
109 }
110 }
111}