hl7_parser/builder/
segment.rs

1use display::SegmentBuilderDisplay;
2
3use crate::message::Segment;
4use std::{collections::HashMap, fmt::Display};
5
6use super::FieldBuilder;
7
8/// A builder for constructing HL7 segments.
9#[derive(Debug, Clone, PartialEq, Eq)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11#[derive(Default)]
12pub struct SegmentBuilder {
13    pub name: String,
14    pub fields: HashMap<usize, FieldBuilder>,
15}
16
17impl SegmentBuilder {
18    /// Create a new segment builder with the given name. No fields are added.
19    pub fn new<S: ToString>(name: S) -> Self {
20        SegmentBuilder {
21            name: name.to_string(),
22            fields: HashMap::new(),
23        }
24    }
25
26    /// Append a field to the segment. Fields will be output in the order they are added.
27    pub fn push_field(&mut self, field: FieldBuilder) {
28        let index = self.fields.len();
29        self.fields.insert(index + 1, field);
30    }
31
32    /// Get the name of the segment.
33    pub fn name(&self) -> &str {
34        &self.name
35    }
36
37    /// Get the fields in the segment.
38    pub fn fields(&self) -> &HashMap<usize, FieldBuilder> {
39        &self.fields
40    }
41
42    /// Get a mutable reference to the fields in the segment.
43    pub fn fields_mut(&mut self) -> &mut HashMap<usize, FieldBuilder> {
44        &mut self.fields
45    }
46
47    /// Get a field by index (1-based).
48    pub fn field(&self, index: usize) -> Option<&FieldBuilder> {
49        debug_assert!(index > 0, "Field numbers are 1-based");
50        self.fields.get(&index)
51    }
52
53    /// Get a mutable reference to a field by index (1-based).
54    pub fn field_mut(&mut self, index: usize) -> Option<&mut FieldBuilder> {
55        debug_assert!(index > 0, "Field numbers are 1-based");
56        self.fields.get_mut(&index)
57    }
58
59    /// Remove a field by index (1-based).
60    pub fn remove_field(&mut self, index: usize) -> Option<FieldBuilder> {
61        debug_assert!(index > 0, "Field numbers are 1-based");
62        self.fields.remove(&index)
63    }
64
65    /// Get the first field with the given index.
66    pub fn has_field(&self, index: usize) -> bool {
67        debug_assert!(index > 0, "Field numbers are 1-based");
68        self.fields.contains_key(&index)
69    }
70
71    /// Check if the segment has no fields.
72    pub fn is_empty(&self) -> bool {
73        self.fields.is_empty()
74    }
75
76    /// Clear all fields from the segment.
77    pub fn clear(&mut self) {
78        self.fields.clear();
79    }
80
81    /// Set the name of the segment.
82    pub fn set_name<S: ToString>(&mut self, name: S) {
83        self.name = name.to_string();
84    }
85
86    /// Set a field in the segment. (1-based)
87    pub fn set_field(&mut self, index: usize, field: FieldBuilder) {
88        debug_assert!(index > 0, "Field numbers are 1-based");
89        self.fields.insert(index, field);
90    }
91
92    /// Set the value of a field in the segment. (1-based)
93    pub fn set_field_value<S: ToString>(&mut self, index: usize, value: S) {
94        debug_assert!(index > 0, "Field numbers are 1-based");
95        let field = self.fields.entry(index).or_default();
96        field.set_value(value.to_string());
97    }
98
99    /// Add a field to the segment. (1-based)
100    pub fn with_field<F: Into<FieldBuilder>>(mut self, index: usize, field: F) -> Self {
101        self.set_field(index, field.into());
102        self
103    }
104
105    /// Add a field with a value to the segment. (1-based)
106    pub fn with_field_value<S: ToString>(mut self, index: usize, value: S) -> Self {
107        self.set_field_value(index, value);
108        self
109    }
110
111    /// Display the segment using the given separators.
112    pub fn display<'a>(&'a self, separators: &'a super::Separators) -> SegmentBuilderDisplay<'a> {
113        SegmentBuilderDisplay {
114            segment: self,
115            separators,
116        }
117    }
118}
119
120mod display {
121    use super::*;
122    use crate::message::Separators;
123
124    /// Display implementation for `SegmentBuilder`, to render the segment as a string.
125    pub struct SegmentBuilderDisplay<'a> {
126        pub(super) segment: &'a SegmentBuilder,
127        pub(super) separators: &'a Separators,
128    }
129
130    impl<'a> Display for SegmentBuilderDisplay<'a> {
131        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132            write!(f, "{}", self.segment.name)?;
133
134            if self.segment.fields.is_empty() {
135                return Ok(());
136            }
137
138            let start_index = if self.segment.name == "MSH" {
139                write!(f, "{}", self.separators)?;
140                3
141            } else {
142                1
143            };
144            write!(f, "{}", self.separators.field)?;
145            let max_index = self.segment.fields.keys().max().unwrap();
146            for i in start_index..=*max_index {
147                if let Some(field) = self.segment.fields.get(&i) {
148                    write!(f, "{}", field.display(self.separators))?;
149                }
150                if i < *max_index {
151                    write!(f, "{}", self.separators.field)?;
152                }
153            }
154            Ok(())
155        }
156    }
157}
158
159impl<'m> From<&'m Segment<'m>> for SegmentBuilder {
160    fn from(segment: &'m Segment) -> Self {
161        let mut builder = SegmentBuilder::new(segment.name);
162        builder.fields = segment
163            .fields
164            .iter()
165            .enumerate()
166            .map(|(index, field)| (index + 1, field.into()))
167            .collect();
168        builder
169    }
170}
171
172#[cfg(test)]
173mod tests {
174    use crate::message::Separators;
175
176    use super::*;
177    use pretty_assertions_sorted::assert_eq;
178
179    #[test]
180    fn can_convert_from_segment() {
181        let segment = crate::parser::parse_segment(r#"PID|1|2|3"#).unwrap();
182        let builder: SegmentBuilder = SegmentBuilder::from(&segment);
183        assert_eq!(builder.name(), "PID");
184        assert_eq!(builder.fields().len(), 3);
185        assert_eq!(builder.field(1).unwrap().value().unwrap(), "1");
186        assert_eq!(builder.field(2).unwrap().value().unwrap(), "2");
187        assert_eq!(builder.field(3).unwrap().value().unwrap(), "3");
188    }
189
190    #[test]
191    fn can_display_segment() {
192        let segment = crate::parser::parse_segment(r#"PID|1|2|3"#).unwrap();
193        let builder: SegmentBuilder = SegmentBuilder::from(&segment);
194        let separators = Separators::default();
195        let display = builder.display(&separators).to_string();
196        assert_eq!(display, r#"PID|1|2|3"#);
197    }
198}