rusthl7/
fields.rs

1use super::separators::Separators;
2use super::*;
3use std::fmt::Display;
4use std::ops::Index;
5
6/// Represents a single field inside the HL7.  Note that fields can include repeats, components and sub-components.
7/// See [the spec](http://www.hl7.eu/HL7v2x/v251/std251/ch02.html#Heading13) for more info
8#[derive(Debug, PartialEq)]
9pub struct Field<'a> {
10    pub source: &'a str,
11    pub delims: Separators,
12    pub repeats: Vec<&'a str>,
13    pub components: Vec<Vec<&'a str>>,
14    pub subcomponents: Vec<Vec<Vec<&'a str>>>,
15}
16
17impl<'a> Field<'a> {
18    /// Convert the given line of text into a field.
19    pub fn parse<S: Into<&'a str>>(
20        input: S,
21        delims: &Separators,
22    ) -> Result<Field<'a>, Hl7ParseError> {
23        let input = input.into();
24        let repeats: Vec<&'a str> = input.split(delims.repeat).collect();
25        let components: Vec<Vec<&'a str>> = repeats
26            .iter()
27            .map(|r| r.split(delims.component).collect::<Vec<&'a str>>())
28            .collect();
29        let subcomponents: Vec<Vec<Vec<&'a str>>> = components
30            .iter()
31            .map(|r| {
32                r.iter()
33                    .map(|c| c.split(delims.subcomponent).collect::<Vec<&'a str>>())
34                    .collect::<Vec<Vec<&'a str>>>()
35            })
36            .collect();
37        let field = Field {
38            source: input,
39            delims: *delims,
40            repeats,
41            components,
42            subcomponents,
43        };
44        Ok(field)
45    }
46
47    /// Used to hide the removal of NoneError for #2...  If passed `Some()` value it returns a field with that value.  If passed `None() it returns an `Err(Hl7ParseError::MissingRequiredValue{})`
48    pub fn parse_mandatory(
49        input: Option<&'a str>,
50        delims: &Separators,
51    ) -> Result<Field<'a>, Hl7ParseError> {
52        match input {
53            Some(string_value) => Field::parse(string_value, delims),
54            None => Err(Hl7ParseError::MissingRequiredValue {}),
55        }
56    }
57
58    /// Converts a possibly blank string into a possibly blank field!  
59    /// Note this handles optional fields, not the nul (`""`) value.
60    pub fn parse_optional(
61        input: Option<&'a str>,
62        delims: &Separators,
63    ) -> Result<Option<Field<'a>>, Hl7ParseError> {
64        match input {
65            None => Ok(None),
66            Some(x) if x.is_empty() => Ok(None),
67            Some(x) => Ok(Some(Field::parse(x, delims)?)),
68        }
69    }
70
71    /// Compatibility method to get the underlying value of this field.
72    #[inline]
73    pub fn value(&self) -> &'a str {
74        self.source
75    }
76
77    /// Export value to str
78    #[inline]
79    pub fn as_str(&self) -> &'a str {
80        self.source
81    }
82
83    /// Access string reference of a Field component by String index
84    /// Adjust the index by one as medical people do not count from zero
85    pub fn query<'b, S>(&self, sidx: S) -> &'a str
86    where
87        S: Into<&'b str>,
88    {
89        let sidx = sidx.into();
90        let parts = sidx.split('.').collect::<Vec<&str>>();
91
92        if parts.len() == 1 {
93            let stringnums = parts[0]
94                .chars()
95                .filter(|c| c.is_digit(10))
96                .collect::<String>();
97            let idx: usize = stringnums.parse().unwrap();
98
99            self[idx - 1]
100        } else if parts.len() == 2 {
101            let stringnums = parts[0]
102                .chars()
103                .filter(|c| c.is_digit(10))
104                .collect::<String>();
105
106            let idx0: usize = stringnums.parse().unwrap();
107
108            let stringnums = parts[1]
109                .chars()
110                .filter(|c| c.is_digit(10))
111                .collect::<String>();
112
113            let idx1: usize = stringnums.parse().unwrap();
114
115            self[(idx0 - 1, idx1 - 1)]
116        } else {
117            ""
118        }
119    }
120}
121
122impl<'a> Display for Field<'a> {
123    /// Required for to_string() and other formatter consumers
124    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125        write!(f, "{}", self.source)
126    }
127}
128
129impl<'a> Clone for Field<'a> {
130    /// Creates a new Message object using a clone of the original's source
131    fn clone(&self) -> Self {
132        Field::parse(self.source, &self.delims.clone()).unwrap()
133    }
134}
135
136impl<'a> Index<usize> for Field<'a> {
137    type Output = &'a str;
138    /// Access string reference of a Field component by numeric index
139    fn index(&self, idx: usize) -> &Self::Output {
140        if idx > self.repeats.len() - 1 {
141            return &""; //TODO: We're returning &&str here which doesn't seem right?!?
142        }
143
144        &self.repeats[idx]
145    }
146}
147
148impl<'a> Index<(usize, usize)> for Field<'a> {
149    type Output = &'a str;
150    /// Access string reference of a Field subcomponent by numeric index
151    fn index(&self, idx: (usize, usize)) -> &Self::Output {
152        if idx.0 > self.repeats.len() - 1 || idx.1 > self.components[idx.0].len() - 1 {
153            return &""; //TODO: We're returning &&str here which doesn't seem right?!?
154        }
155
156        &self.components[idx.0][idx.1]
157    }
158}
159
160impl<'a> Index<(usize, usize, usize)> for Field<'a> {
161    type Output = &'a str;
162    /// Access string reference of a Field subcomponent by numeric index
163    fn index(&self, idx: (usize, usize, usize)) -> &Self::Output {
164        if idx.0 > self.repeats.len() - 1
165            || idx.1 > self.components[idx.0].len() - 1
166            || idx.2 > self.subcomponents[idx.0][idx.1].len() - 1
167        {
168            return &""; //TODO: We're returning &&str here which doesn't seem right?!?
169        }
170
171        &self.subcomponents[idx.0][idx.1][idx.2]
172    }
173}
174
175#[cfg(feature = "string_index")]
176impl<'a> Index<String> for Field<'a> {
177    type Output = &'a str;
178
179    /// Access string reference of a Field component by String index
180    #[cfg(feature = "string_index")]
181    fn index(&self, sidx: String) -> &Self::Output {
182        let parts = sidx.split('.').collect::<Vec<&str>>();
183        match parts.len() {
184            1 => {
185                let stringnums = parts[0]
186                    .chars()
187                    .filter(|c| c.is_digit(10))
188                    .collect::<String>();
189                let idx: usize = stringnums.parse().unwrap();
190
191                &self[idx - 1]
192            }
193            2 => {
194                let stringnums = parts[0]
195                    .chars()
196                    .filter(|c| c.is_digit(10))
197                    .collect::<String>();
198
199                let idx0: usize = stringnums.parse().unwrap();
200
201                let stringnums = parts[1]
202                    .chars()
203                    .filter(|c| c.is_digit(10))
204                    .collect::<String>();
205
206                let idx1: usize = stringnums.parse().unwrap();
207
208                &self[(idx0 - 1, idx1 - 1)]
209            }
210            3 => {
211                let stringnums = parts[0]
212                    .chars()
213                    .filter(|c| c.is_digit(10))
214                    .collect::<String>();
215
216                let idx0: usize = stringnums.parse().unwrap();
217
218                let stringnums = parts[1]
219                    .chars()
220                    .filter(|c| c.is_digit(10))
221                    .collect::<String>();
222
223                let idx1: usize = stringnums.parse().unwrap();
224
225                let stringnums = parts[2]
226                    .chars()
227                    .filter(|c| c.is_digit(10))
228                    .collect::<String>();
229
230                let idx2: usize = stringnums.parse().unwrap();
231
232                &self[(idx0 - 1, idx1 - 1, idx2 - 1)]
233            }
234            _ => &"",
235        }
236    }
237}
238
239#[cfg(feature = "string_index")]
240impl<'a> Index<&str> for Field<'a> {
241    type Output = &'a str;
242
243    /// Access Segment, Field, or sub-field string references by string index
244    #[cfg(feature = "string_index")]
245    fn index(&self, idx: &str) -> &Self::Output {
246        &self[String::from(idx)]
247    }
248}
249
250#[cfg(test)]
251mod tests {
252    use super::*;
253
254    #[test]
255    fn test_conditional_parse_handles_none() {
256        let d = Separators::default();
257
258        //if we pass a none value, we get a None back
259        match Field::parse_optional(None, &d) {
260            Ok(None) => assert!(true),
261            _ => assert!(false),
262        }
263    }
264
265    #[test]
266    fn test_conditional_parse_handles_empty_string() {
267        let d = Separators::default();
268
269        //an empty string (as seen when `split()`ing) should be none
270        match Field::parse_optional(Some(""), &d) {
271            Ok(None) => assert!(true),
272            _ => assert!(false),
273        }
274    }
275
276    #[test]
277    fn test_conditional_parse_handles_value_string() {
278        let d = Separators::default();
279
280        //an empty string (as seen when `split()`ing) should be none
281        match Field::parse_optional(Some("xxx"), &d) {
282            Ok(Some(field)) => assert_eq!(field.value(), "xxx"),
283            _ => assert!(false),
284        }
285    }
286
287    #[test]
288    fn test_parse_mandatory_handles_some_value() {
289        let d = Separators::default();
290
291        match Field::parse_mandatory(Some("xxx"), &d) {
292            Ok(field) => assert_eq!(field.value(), "xxx"),
293            _ => assert!(false),
294        }
295    }
296
297    #[test]
298    fn test_parse_mandatory_throws_on_none() {
299        let d = Separators::default();
300
301        match Field::parse_mandatory(None, &d) {
302            Err(Hl7ParseError::MissingRequiredValue()) => assert!(true),
303            _ => assert!(false),
304        }
305    }
306    #[test]
307    fn test_parse_repeats() {
308        let d = Separators::default();
309        let f = Field::parse_mandatory(Some("x&x^y&y~a&a^b&b"), &d).unwrap();
310        assert_eq!(f.repeats.len(), 2)
311    }
312
313    #[test]
314    fn test_parse_components() {
315        let d = Separators::default();
316        let f = Field::parse_mandatory(Some("xxx^yyy"), &d).unwrap();
317        assert_eq!(f.components[0].len(), 2)
318    }
319
320    #[test]
321    fn test_parse_subcomponents() {
322        let d = Separators::default();
323        let f = Field::parse_mandatory(Some("xxx^yyy&zzz"), &d).unwrap();
324        assert_eq!(f.subcomponents[0][1].len(), 2)
325    }
326
327    #[test]
328    fn test_to_string() {
329        let d = Separators::default();
330        let f = Field::parse_mandatory(Some("xxx^yyy&zzz"), &d).unwrap();
331        assert_eq!(f.to_string(), String::from("xxx^yyy&zzz"))
332    }
333
334    #[test]
335    fn test_clone() {
336        let d = Separators::default();
337        let f = Field::parse_mandatory(Some("xxx^yyy&zzz"), &d).unwrap();
338        assert_eq!(f.to_string(), f.clone().as_str())
339    }
340
341    #[test]
342    fn test_uint_index() {
343        let d = Separators::default();
344        let f = Field::parse_mandatory(Some("xxx^yyy&zzz"), &d).unwrap();
345        assert_eq!(f[(0, 1)], "yyy&zzz");
346        assert_eq!(f[(0, 1, 1)], "zzz");
347    }
348
349    #[test]
350    fn test_string_query() {
351        let d = Separators::default();
352        let f = Field::parse_mandatory(Some("x&x^y&y~a&a^b&b"), &d).unwrap();
353        let idx0 = String::from("R2");
354        let oob = "R2.C3";
355        assert_eq!(f.query(&*idx0), "a&a^b&b");
356        assert_eq!(f.query("R2.C2"), "b&b");
357        assert_eq!(f.query(oob), "");
358    }
359
360    #[cfg(feature = "string_index")]
361    mod string_index_tests {
362        use super::*;
363        #[test]
364        fn test_string_index() {
365            let d = Separators::default();
366            let f = Field::parse_mandatory(Some("x&x^y&y~a&a^b&b"), &d).unwrap();
367            assert_eq!(f["R2"], "a&a^b&b");
368            assert_eq!(f["R2.C2"], "b&b");
369            assert_eq!(f["R2.C3"], "");
370        }
371    }
372}