hl7_parser/builder/
mod.rs

1//! # HL7 Message Builder
2//!
3//! This module provides a builder for constructing HL7 messages. The builder can be used to
4//! construct messages from scratch, or to modify existing messages.
5//!
6//! ## Examples
7//!
8//! ```
9//! use hl7_parser::builder::prelude::*;
10//!
11//! let message = MessageBuilder::new(Separators::default())
12//!     .with_segment(SegmentBuilder::new("MSH")
13//!         .with_field_value(3, "SendingApp")
14//!         .with_field_value(4, "SendingFac")
15//!         .with_field_value(5, "ReceivingApp")
16//!         .with_field_value(6, "ReceivingFac")
17//!         .with_field(9,
18//!             FieldBuilder::default()
19//!                 .with_component(1, "ADT")
20//!                 .with_component(2, "A01"))
21//!         .with_field_value(10, "123456")
22//!         .with_field_value(11, "P")
23//!         .with_field_value(12, "2.3"))
24//!     .with_segment(SegmentBuilder::new("PID")
25//!         .with_field_value(3, "123456")
26//!         .with_field(5,
27//!             FieldBuilder::default()
28//!                 .with_component(1, "Doe")
29//!                 .with_component(2, "John"))
30//!         .with_field_value(7, "19700101"))
31//!     .render_with_newlines().to_string();
32//!
33//! assert_eq!(message,
34//! "MSH|^~\\&|SendingApp|SendingFac|ReceivingApp|ReceivingFac|||ADT^A01|123456|P|2.3\nPID|||123456||Doe^John||19700101");
35//! ```
36
37mod segment;
38use std::fmt::Display;
39
40use display::MessageBuilderDisplay;
41pub use segment::*;
42
43mod field;
44pub use field::*;
45
46mod repeat;
47pub use repeat::*;
48
49mod component;
50pub use component::*;
51
52use crate::{message::Separators, Message};
53
54/// Prelude for building HL7 messages.
55pub mod prelude {
56    pub use super::*;
57    pub use crate::message::Separators;
58}
59
60/// A builder for constructing HL7 messages.
61#[derive(Debug, Clone, PartialEq, Eq)]
62#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
63#[derive(Default)]
64pub struct MessageBuilder {
65    separators: Separators,
66    segments: Vec<SegmentBuilder>,
67}
68
69impl MessageBuilder {
70    /// Create a new message builder with the given separators. No segments are added.
71    pub fn new(separators: Separators) -> Self {
72        MessageBuilder {
73            separators,
74            segments: Vec::new(),
75        }
76    }
77
78    /// Append a segment to the message. Segments will be output in the order they are added.
79    pub fn push_segment(&mut self, segment: SegmentBuilder) {
80        self.segments.push(segment);
81    }
82
83    /// Get the separators used by the message.
84    pub fn separators(&self) -> &Separators {
85        &self.separators
86    }
87
88    /// Get the segments in the message.
89    pub fn segments(&self) -> &[SegmentBuilder] {
90        &self.segments
91    }
92
93    /// Get a mutable reference to the segments in the message.
94    pub fn segments_mut(&mut self) -> &mut Vec<SegmentBuilder> {
95        &mut self.segments
96    }
97
98    /// Get a segment by index (0-based).
99    pub fn segment(&self, index: usize) -> Option<&SegmentBuilder> {
100        self.segments.get(index)
101    }
102
103    /// Get a mutable reference to a segment by index (0-based).
104    pub fn segment_mut(&mut self, index: usize) -> Option<&mut SegmentBuilder> {
105        self.segments.get_mut(index)
106    }
107
108    /// Remove a segment by index (0-based).
109    pub fn remove_segment(&mut self, index: usize) -> Option<SegmentBuilder> {
110        if index >= self.segments.len() {
111            return None;
112        }
113        Some(self.segments.remove(index))
114    }
115
116    /// Get the first segment with the given name.
117    pub fn segment_named<S: AsRef<str>>(&self, name: S) -> Option<&SegmentBuilder> {
118        self.segments
119            .iter()
120            .find(|segment| segment.name() == name.as_ref())
121    }
122
123    /// Get a mutable reference to the first segment with the given name.
124    pub fn segment_named_mut<S: AsRef<str>>(&mut self, name: S) -> Option<&mut SegmentBuilder> {
125        self.segments
126            .iter_mut()
127            .find(|segment| segment.name() == name.as_ref())
128    }
129
130    /// Remove the first segment with the given name.
131    pub fn remove_segment_named<S: AsRef<str>>(&mut self, name: S) -> Option<SegmentBuilder> {
132        let index = self
133            .segments
134            .iter()
135            .position(|segment| segment.name() == name.as_ref())?;
136        Some(self.segments.remove(index))
137    }
138
139    /// Get the nth segment with the given name (1-based).
140    pub fn segment_n<S: AsRef<str>>(&self, name: S, n: usize) -> Option<&SegmentBuilder> {
141        debug_assert!(n > 0, "Segment numbers are 1-based");
142        let name = name.as_ref();
143        self.segments
144            .iter()
145            .filter(|s| s.name.as_str() == name)
146            .nth(n - 1)
147    }
148
149    /// Get a mutable reference to the nth segment with the given name (1-based).
150    pub fn segment_n_mut<S: AsRef<str>>(
151        &mut self,
152        name: S,
153        n: usize,
154    ) -> Option<&mut SegmentBuilder> {
155        debug_assert!(n > 0, "Segment numbers are 1-based");
156        let name = name.as_ref();
157        self.segments
158            .iter_mut()
159            .filter(|s| s.name.as_str() == name)
160            .nth(n - 1)
161    }
162
163    /// Remove the nth segment with the given name (1-based).
164    pub fn remove_segment_n<S: AsRef<str>>(&mut self, name: S, n: usize) -> Option<SegmentBuilder> {
165        debug_assert!(n > 0, "Segment numbers are 1-based");
166        let name = name.as_ref();
167        let index = self
168            .segments
169            .iter()
170            .enumerate()
171            .find(|(_, s)| s.name.as_str() == name)
172            .map(|(i, _)| i)?;
173        Some(self.segments.remove(index))
174    }
175
176    /// Check if the message is empty (i.e. has no segments).
177    pub fn is_empty(&self) -> bool {
178        self.segments.is_empty()
179    }
180
181    /// Clear all segments from the message.
182    pub fn clear(&mut self) {
183        self.segments.clear();
184    }
185
186    /// Set the separators used by the message.
187    pub fn set_separators(&mut self, separators: Separators) {
188        self.separators = separators;
189    }
190
191    /// Add a segment to the message.
192    pub fn with_segment(mut self, segment: SegmentBuilder) -> Self {
193        self.push_segment(segment);
194        self
195    }
196
197    /// Display the message with the default line endings for the current platform.
198    ///
199    /// On Windows, this will use `\r\n` as the line ending. On other platforms, it will use `\n`.
200    pub fn render_with_newlines(&self) -> MessageBuilderDisplay<'_> {
201        MessageBuilderDisplay {
202            message: self,
203            #[cfg(windows)]
204            line_endings: "\r\n",
205            #[cfg(not(windows))]
206            line_endings: "\n",
207        }
208    }
209
210    /// Display the message with the given line endings.
211    pub fn render_with_segment_separators<'a>(
212        &'a self,
213        line_endings: &'a str,
214    ) -> MessageBuilderDisplay<'a> {
215        MessageBuilderDisplay {
216            message: self,
217            line_endings,
218        }
219    }
220}
221
222/// Render the message using the proper `\r` segment separators.
223impl Display for MessageBuilder {
224    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225        let mut first = true;
226        for segment in &self.segments {
227            if first {
228                first = false;
229            } else {
230                write!(f, "\r")?;
231            }
232            write!(f, "{}", segment.display(&self.separators))?;
233        }
234        Ok(())
235    }
236}
237
238mod display {
239    use super::*;
240
241    /// Display the message with the given (non-standard) line endings.
242    pub struct MessageBuilderDisplay<'a> {
243        pub(super) message: &'a MessageBuilder,
244        pub(super) line_endings: &'a str,
245    }
246
247    impl<'a> Display for MessageBuilderDisplay<'a> {
248        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
249            let mut first = true;
250            for segment in &self.message.segments {
251                if first {
252                    first = false;
253                } else {
254                    write!(f, "{}", self.line_endings)?;
255                }
256                write!(f, "{}", segment.display(&self.message.separators))?;
257            }
258            Ok(())
259        }
260    }
261}
262
263/// Convert a message into a message builder.
264impl<'m> From<&'m Message<'m>> for MessageBuilder {
265    fn from(message: &'m Message) -> Self {
266        let mut builder = MessageBuilder::new(message.separators);
267        builder.segments = message.segments().map(SegmentBuilder::from).collect();
268        builder
269    }
270}
271
272#[cfg(test)]
273mod tests {
274    use super::*;
275    use pretty_assertions_sorted::assert_eq;
276
277    #[test]
278    fn can_roundtrip_messages() {
279        let message_src = include_str!("../../test_assets/sample_adt_a01.hl7");
280        let message = crate::parser::parse_message_with_lenient_newlines(message_src, true)
281            .expect("Can parse message");
282
283        let builder: MessageBuilder = MessageBuilder::from(&message);
284        let display = builder.render_with_newlines().to_string();
285        assert_eq!(message_src.trim(), display);
286    }
287}