hl7_parser/message/
component.rs

1use crate::display::ComponentDisplay;
2
3use super::{Separators, Subcomponent};
4use std::ops::Range;
5
6/// A component is a part of a field, and is separated from other components by the component
7/// separator character. A component is composed of 0 or more subcomponents.
8#[derive(Debug, Clone, PartialEq, Eq)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize))]
10pub struct Component<'m> {
11    pub(crate) source: &'m str,
12    /// The subcomponents of the component
13    pub subcomponents: Vec<Subcomponent<'m>>,
14    /// The range of the component in the original message
15    pub range: Range<usize>,
16}
17
18impl<'m> Component<'m> {
19    pub(crate) fn new_single(source: &'m str, range: Range<usize>) -> Self {
20        Component {
21            source,
22            subcomponents: vec![Subcomponent::new_single(source, range.clone())],
23            range,
24        }
25    }
26
27    #[inline]
28    /// An iterator over the subcomponents of the component
29    pub fn subcomponents(&self) -> impl Iterator<Item = &Subcomponent<'m>> {
30        self.subcomponents.iter()
31    }
32
33    #[inline]
34    /// Display the component value, using the separators to decode escape sequences
35    /// by default. Note: if you want to display the raw value without decoding escape
36    /// sequences, use the `#` flag, e.g. `format!("{:#}", component.display(separators))`.
37    /// Components will be separated by the component separator character.
38    pub fn display(&'m self, separators: &'m Separators) -> ComponentDisplay<'m> {
39        ComponentDisplay {
40            subcomponents: &self.subcomponents,
41            separators,
42        }
43    }
44
45    #[inline]
46    /// Get the raw value of the component. This is the value as it appears in the message,
47    /// without any decoding of escape sequences, and including all subcomponents and
48    /// their separators.
49    ///
50    /// # Examples
51    ///
52    /// ```
53    /// let component = hl7_parser::parser::parse_component("foo&bar").unwrap();
54    /// assert_eq!(component.subcomponents.len(), 2);
55    /// assert_eq!(component.raw_value(), "foo&bar");
56    /// ```
57    pub fn raw_value(&self) -> &'m str {
58        self.source
59    }
60
61    #[inline]
62    /// Returns true if the component has more than one subcomponent. Note that
63    /// if the component has only one subcomponent, the value of that subcomponent
64    /// is essentially the value of the component, so the value of the component
65    /// can be obtained using `raw_value()`.
66    ///
67    /// # Examples
68    ///
69    /// ```
70    /// let component = hl7_parser::parser::parse_component("foo&bar").unwrap();
71    /// assert_eq!(component.has_subcomponents(), true);
72    /// let component = hl7_parser::parser::parse_component("foo").unwrap();
73    /// assert_eq!(component.has_subcomponents(), false);
74    /// ```
75    pub fn has_subcomponents(&self) -> bool {
76        self.subcomponents.len() > 1
77    }
78
79    /// Returns true if the component has no subcomponents, or if all subcomponents
80    /// have empty values.
81    ///
82    /// # Examples
83    ///
84    /// ```
85    /// let component = hl7_parser::parser::parse_component("foo&bar").unwrap();
86    /// assert_eq!(component.is_empty(), false);
87    /// let component = hl7_parser::parser::parse_component("foo").unwrap();
88    /// assert_eq!(component.is_empty(), false);
89    /// let component = hl7_parser::parser::parse_component("foo&").unwrap();
90    /// assert_eq!(component.is_empty(), false);
91    /// let component = hl7_parser::parser::parse_component("").unwrap();
92    /// assert_eq!(component.is_empty(), true);
93    /// let component = hl7_parser::parser::parse_component("&").unwrap();
94    /// assert_eq!(component.is_empty(), true);
95    /// ```
96    pub fn is_empty(&self) -> bool {
97        self.subcomponents.is_empty() || self.subcomponents.iter().all(|s| s.value.is_empty())
98    }
99
100    /// Get a subcomponent by its number. Subcomponent numbers are 1-based.
101    /// Returns `None` if the subcomponent number is out of range.
102    ///
103    /// # Examples
104    ///
105    /// ```
106    /// let component = hl7_parser::parser::parse_component("foo&bar").unwrap();
107    /// assert_eq!(component.subcomponent(1).unwrap().value, "foo");
108    /// assert_eq!(component.subcomponent(2).unwrap().value, "bar");
109    /// assert_eq!(component.subcomponent(3), None);
110    /// ```
111    pub fn subcomponent(&self, number: usize) -> Option<&Subcomponent<'m>> {
112        debug_assert!(number > 0, "Subcomponent numbers are 1-based");
113        self.subcomponents.get(number - 1)
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[test]
122    fn can_get_component_raw_value() {
123        let component = Component {
124            source: "foo&bar",
125            subcomponents: vec![
126                Subcomponent {
127                    value: "foo",
128                    range: 0..1, // ignore
129                },
130                Subcomponent {
131                    value: "bar",
132                    range: 0..1, // ignore
133                },
134            ],
135            range: 0..1, // ignore
136        };
137
138        assert_eq!(component.raw_value(), "foo&bar");
139    }
140
141    #[test]
142    fn components_can_display_raw() {
143        let component = Component {
144            source: r"foo\F\bar&baz",
145            subcomponents: vec![
146                Subcomponent {
147                    value: r"foo\F\bar",
148                    range: 0..1, // ignore
149                },
150                Subcomponent {
151                    value: r"baz",
152                    range: 0..1, // ignore
153                },
154            ],
155            range: 0..1, // ignore
156        };
157
158        let formatted = format!("{:#}", component.display(&Separators::default()));
159        assert_eq!(formatted, r"foo\F\bar&baz");
160    }
161
162    #[test]
163    fn components_can_display_decoded() {
164        let component = Component {
165            source: r"foo\F\bar&baz",
166            subcomponents: vec![
167                Subcomponent {
168                    value: r"foo\F\bar",
169                    range: 0..1, // ignore
170                },
171                Subcomponent {
172                    value: r"baz",
173                    range: 0..1, // ignore
174                },
175            ],
176            range: 0..1, // ignore
177        };
178
179        let formatted = format!("{}", component.display(&Separators::default()));
180        assert_eq!(formatted, "foo|bar&baz");
181    }
182}