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 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}