Skip to main content

rumtk_hl7_v2/
lib.rs

1/*
2 * rumtk attempts to implement HL7 and medical protocols for interoperability in medicine.
3 * This toolkit aims to be reliable, simple, performant, and standards compliant.
4 * Copyright (C) 2024  Luis M. Santos, M.D.
5 * Copyright (C) 2025  MedicalMasses L.L.C.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21//#![feature(inherent_associated_types)]
22#![feature(rustc_private)]
23#![feature(str_as_str)]
24extern crate rumtk_core;
25pub mod hl7_v2_base_types;
26pub mod hl7_v2_complex_types;
27pub mod hl7_v2_constants;
28pub mod hl7_v2_datasets;
29pub mod hl7_v2_field_descriptors;
30pub mod hl7_v2_interpreter;
31pub mod hl7_v2_mllp;
32mod hl7_v2_optionality_rules;
33pub mod hl7_v2_parser;
34mod hl7_v2_scripting;
35pub mod hl7_v2_search;
36pub mod hl7_v2_types;
37/*****************************************Tests****************************************/
38#[cfg(test)]
39mod tests {
40    use crate::hl7_v2_base_types::v2_base_types::{
41        V2DateTime, V2ParserCharacters, V2SearchIndex, V2String,
42    };
43    use crate::hl7_v2_base_types::v2_primitives::{
44        V2PrimitiveCasting, V2PrimitiveType, TRUNCATE_FT,
45    };
46    use crate::hl7_v2_complex_types::hl7_v2_complex_types::{cast_component, V2Type};
47    use crate::hl7_v2_constants::{V2_SEGMENT_IDS, V2_SEGMENT_NAMES};
48    use crate::hl7_v2_field_descriptors::v2_field_descriptor::{
49        V2ComponentType, V2ComponentTypeDescriptor,
50    };
51    use crate::hl7_v2_mllp::mllp_v2::{mllp_decode, mllp_encode, CR, EB, MLLP_FILTER_POLICY, SB};
52    use crate::hl7_v2_optionality_rules::Optionality;
53    use crate::hl7_v2_parser::v2_parser::{V2Field, V2Message};
54    use crate::hl7_v2_search::REGEX_V2_SEARCH_DEFAULT;
55    use crate::{
56        rumtk_v2_find_component, rumtk_v2_generate_message, rumtk_v2_mllp_connect,
57        rumtk_v2_mllp_get_client_ids, rumtk_v2_mllp_get_ip_port, rumtk_v2_mllp_iter_channels,
58        rumtk_v2_mllp_listen, rumtk_v2_mllp_send, rumtk_v2_parse_message,
59    };
60    use rumtk_core::core::RUMResult;
61    use rumtk_core::search::rumtk_search::{string_search_named_captures, SearchGroups};
62    use rumtk_core::strings::{
63        basic_escape, rumtk_format, AsStr, RUMArrayConversions, RUMString, RUMStringConversions,
64        StringUtils,
65    };
66    use rumtk_core::{
67        rumtk_create_task, rumtk_deserialize, rumtk_exec_task, rumtk_init_threads, rumtk_serialize,
68        rumtk_sleep,
69    };
70    use std::thread::spawn;
71    /**********************************Constants**************************************/
72    use crate::hl7_v2_datasets::{hl7_v2_messages::*, hl7_v2_test_fragments::*};
73
74    /*********************************Test Cases**************************************/
75    #[test]
76    fn test_hl7_v2_field_parsing() {
77        let field_str = DEFAULT_HL7_V2_FIELD_STRING;
78        let encode_chars = V2ParserCharacters::new();
79        let field = V2Field::from_str(&field_str, &encode_chars);
80        println!("{:#?}", &field);
81        assert_eq!(field.len(), 3, "Wrong number of components in field");
82        println!(
83            "Value in component {} => {}!",
84            0,
85            field.get(1).unwrap().as_str()
86        );
87        assert_eq!(
88            field.get(1).unwrap().as_str(),
89            "2000",
90            "Wrong value in component!"
91        );
92        println!(
93            "Value in component {} => {}!",
94            1,
95            field.get(2).unwrap().as_str()
96        );
97        assert_eq!(
98            field.get(2).unwrap().as_str(),
99            "2012",
100            "Wrong value in component!"
101        );
102        println!(
103            "Value in component {} => {}!",
104            2,
105            field.get(3).unwrap().as_str()
106        );
107        assert_eq!(
108            field.get(3).unwrap().as_str(),
109            "01",
110            "Wrong value in component!"
111        );
112    }
113
114    #[test]
115    fn test_sanitize_hl7_v2_message() {
116        let message = DEFAULT_HL7_V2_MESSAGE;
117        let sanitized_message = V2Message::sanitize(message);
118        println!("{}", message);
119        println!("{}", sanitized_message);
120        assert!(
121            message.contains('\n'),
122            "Raw message has new line characters."
123        );
124        assert!(
125            !sanitized_message.contains('\n'),
126            "Sanitized message has new line characters."
127        );
128        assert!(!sanitized_message.contains("\r\r"), "Sanitizer failed to consolidate double carriage returns into a single carriage return per instance..");
129    }
130
131    #[test]
132    fn test_tokenize_hl7_v2_message() {
133        let message = DEFAULT_HL7_V2_MESSAGE;
134        let sanitized_message = V2Message::sanitize(message);
135        let tokens = V2Message::tokenize_segments(&sanitized_message.as_str());
136        println!("Token count {}", tokens.len());
137        assert_eq!(
138            tokens.len(),
139            5,
140            "Tokenizer generated the wrong number of tokens! We expected 5 segment tokens."
141        );
142    }
143
144    #[test]
145    fn test_load_hl7_v2_encoding_characters() {
146        let message = DEFAULT_HL7_V2_MESSAGE;
147        let sanitized_message = V2Message::sanitize(message);
148        let tokens = V2Message::tokenize_segments(&sanitized_message.as_str());
149        let encode_chars = V2ParserCharacters::from_msh(tokens[0]).unwrap();
150        println!("{:#?}", encode_chars);
151        assert!(
152            encode_chars.segment_terminator.contains('\r'),
153            "Wrong segment character!"
154        );
155        assert!(
156            encode_chars.field_separator.contains('|'),
157            "Wrong field character!"
158        );
159        assert!(
160            encode_chars.component_separator.contains('^'),
161            "Wrong component character!"
162        );
163        assert!(
164            encode_chars.repetition_separator.contains('~'),
165            "Wrong repetition character!"
166        );
167        assert!(
168            encode_chars.escape_character.contains('\\'),
169            "Wrong escape character!"
170        );
171        assert!(
172            encode_chars.subcomponent_separator.contains('&'),
173            "Wrong subcomponent character!"
174        );
175        assert!(
176            encode_chars.truncation_character.contains('#'),
177            "Wrong truncation character!"
178        );
179    }
180
181    #[test]
182    fn test_extract_hl7_v2_message_segments() {
183        let message = DEFAULT_HL7_V2_MESSAGE;
184        let sanitized_message = V2Message::sanitize(message);
185        let tokens = V2Message::tokenize_segments(&sanitized_message.as_str());
186        let msh = V2Message::find_msh(&tokens).unwrap();
187        let encode_chars = V2ParserCharacters::from_msh(&msh.as_str()).unwrap();
188        let parsed_segments = V2Message::extract_segments(&tokens, &encode_chars).unwrap();
189        let keys = parsed_segments.keys();
190        print!("Keys: ");
191        for k in keys {
192            print!("{} ", V2_SEGMENT_NAMES[k]);
193        }
194        assert_eq!(
195            parsed_segments.len(),
196            5,
197            "Number of segments mismatching what was expected!"
198        );
199        assert!(
200            parsed_segments.contains_key(&V2_SEGMENT_IDS["MSH"]),
201            "Missing MSH segment!"
202        );
203        assert!(
204            parsed_segments.contains_key(&V2_SEGMENT_IDS["PID"]),
205            "Missing PID segment!"
206        );
207        assert!(
208            parsed_segments.contains_key(&V2_SEGMENT_IDS["PV1"]),
209            "Missing PV1 segment!"
210        );
211        assert!(
212            parsed_segments.contains_key(&V2_SEGMENT_IDS["EVN"]),
213            "Missing EVN segment!"
214        );
215        assert!(
216            parsed_segments.contains_key(&V2_SEGMENT_IDS["NK1"]),
217            "Missing NK1 segment!"
218        );
219    }
220
221    #[test]
222    fn test_extract_hl7_v2_message_scrambled_segments() {
223        let message = HL7_V2_SCRAMBLED;
224        let sanitized_message = V2Message::sanitize(message);
225        let tokens = V2Message::tokenize_segments(&sanitized_message.as_str());
226        let msh = V2Message::find_msh(&tokens).unwrap();
227        let encode_chars = V2ParserCharacters::from_msh(&msh.as_str()).unwrap();
228        let parsed_segments = V2Message::extract_segments(&tokens, &encode_chars).unwrap();
229        let keys = parsed_segments.keys();
230        print!("Keys: ");
231        for k in keys {
232            print!("{} ", V2_SEGMENT_NAMES[k]);
233        }
234        assert_eq!(
235            parsed_segments.len(),
236            4,
237            "Number of segments mismatching what was expected!"
238        );
239        assert!(
240            parsed_segments.contains_key(&V2_SEGMENT_IDS["MSH"]),
241            "Missing MSH segment!"
242        );
243        assert!(
244            parsed_segments.contains_key(&V2_SEGMENT_IDS["PID"]),
245            "Missing PID segment!"
246        );
247        assert!(
248            parsed_segments.contains_key(&V2_SEGMENT_IDS["PD1"]),
249            "Missing PV1 segment!"
250        );
251        assert!(
252            parsed_segments.contains_key(&V2_SEGMENT_IDS["RXA"]),
253            "Missing EVN segment!"
254        );
255    }
256
257    #[test]
258    fn test_load_hl7_v2_message() {
259        let message = V2Message::from_str(DEFAULT_HL7_V2_MESSAGE);
260        assert!(
261            message.segment_exists(&V2_SEGMENT_IDS["MSH"]),
262            "Missing MSH segment!"
263        );
264        assert!(
265            message.segment_exists(&V2_SEGMENT_IDS["PID"]),
266            "Missing PID segment!"
267        );
268        assert!(
269            message.segment_exists(&V2_SEGMENT_IDS["PV1"]),
270            "Missing PV1 segment!"
271        );
272        assert!(
273            message.segment_exists(&V2_SEGMENT_IDS["EVN"]),
274            "Missing EVN segment!"
275        );
276        assert!(
277            message.segment_exists(&V2_SEGMENT_IDS["NK1"]),
278            "Missing NK1 segment!"
279        );
280    }
281
282    ///
283    /// Per examples in https://confluence.hl7.org/display/OO/v2+Sample+Messages you can have
284    ///  messages that have other header segments before the standard MSH header.
285    ///  As a result, I have made the logic a bit more permissive of the position of the msh segment.
286    ///  I also made sure segments were trimmed to avoid issues with white space padding
287    ///
288    #[test]
289    fn test_load_hl7_v2_message_wir_iis() {
290        let message = V2Message::from_str(HL7_V2_MESSAGE);
291        assert!(
292            message.segment_exists(&V2_SEGMENT_IDS["MSH"]),
293            "Missing MSH segment!"
294        );
295        assert!(
296            message.segment_exists(&V2_SEGMENT_IDS["FHS"]),
297            "Missing FHS segment!"
298        );
299        assert!(
300            message.segment_exists(&V2_SEGMENT_IDS["NK1"]),
301            "Missing NK1 segment!"
302        );
303        assert!(
304            message.segment_exists(&V2_SEGMENT_IDS["PV1"]),
305            "Missing PV1 segment!"
306        );
307        assert!(
308            message.segment_exists(&V2_SEGMENT_IDS["FTS"]),
309            "Missing FTS segment!"
310        );
311        assert!(
312            message.segment_exists(&V2_SEGMENT_IDS["BHS"]),
313            "Missing BHS segment!"
314        );
315    }
316    #[test]
317    fn test_load_hl7_v2_message_scrambled() {
318        let message = V2Message::from_str(HL7_V2_SCRAMBLED);
319        assert!(
320            message.segment_exists(&V2_SEGMENT_IDS["MSH"]),
321            "Missing MSH segment!"
322        );
323        assert!(
324            message.segment_exists(&V2_SEGMENT_IDS["PID"]),
325            "Missing PID segment!"
326        );
327        assert!(
328            message.segment_exists(&V2_SEGMENT_IDS["PD1"]),
329            "Missing PV1 segment!"
330        );
331        assert!(
332            message.segment_exists(&V2_SEGMENT_IDS["RXA"]),
333            "Missing EVN segment!"
334        );
335    }
336
337    ///
338    /// Testing for the proper parsing of message when presented with Unicode portions.
339    ///
340    #[test]
341    fn test_load_hl7_v2_utf8_message() {
342        let message = V2Message::from_str(HL7_V2_PDF_MESSAGE);
343        let pid = message.get(&V2_SEGMENT_IDS["PID"], 1).unwrap();
344        let orc = message.get(&V2_SEGMENT_IDS["ORC"], 1).unwrap();
345        let obr = message.get(&V2_SEGMENT_IDS["OBR"], 1).unwrap();
346        let name1 = pid.get(5).unwrap().get(0).unwrap().get(1).unwrap().as_str();
347        let name2 = orc
348            .get(12)
349            .unwrap()
350            .get(0)
351            .unwrap()
352            .get(3)
353            .unwrap()
354            .as_str();
355        let name3 = obr
356            .get(16)
357            .unwrap()
358            .get(0)
359            .unwrap()
360            .get(3)
361            .unwrap()
362            .as_str();
363        println!("{}", name1);
364        println!("{}", name2);
365        println!("{}", name3);
366        assert_eq!(name1, SPANISH_NAME, "Wrong name/string found in PID(1)5.1!");
367        assert_eq!(
368            name2, SANSKRIT_NAME,
369            "Wrong name/string found in ORC(1)12.3!"
370        );
371        assert_eq!(
372            name3, HIRAGANA_NAME,
373            "Wrong name/string found in OBR(1)16.3!"
374        );
375    }
376
377    ///
378    /// Testing for the proper parsing of message when presented with repeating fields.
379    ///
380    #[test]
381    fn test_handle_hl7_v2_message_with_repeating_fields() {
382        let message = V2Message::from_str(HL7_V2_REPEATING_FIELD_MESSAGE);
383        let msh = message.get(&V2_SEGMENT_IDS["MSH"], 1).unwrap();
384        let field1 = msh
385            .get(-1)
386            .unwrap()
387            .get(0)
388            .unwrap()
389            .get(4)
390            .unwrap()
391            .as_str();
392        let field2 = msh
393            .get(-1)
394            .unwrap()
395            .get(1)
396            .unwrap()
397            .get(1)
398            .unwrap()
399            .as_str();
400        let field3 = msh
401            .get(-1)
402            .unwrap()
403            .get(2)
404            .unwrap()
405            .get(1)
406            .unwrap()
407            .as_str();
408        assert_eq!(
409            msh.get(-1).unwrap().len(),
410            3,
411            "Wrong number of subfields in group in MSH(1)-1!"
412        );
413        assert_eq!(
414            field1, repeate_field1,
415            "Wrong field contents found in MSH(1)-1(0).4!"
416        );
417        assert_eq!(
418            field2, repeate_field2,
419            "Wrong field contents found in MSH(1)-1(1).1!"
420        );
421        assert_eq!(
422            field3, repeate_field3,
423            "Wrong field contents found in MSH(1)-1(2).1!"
424        );
425    }
426
427    #[test]
428    fn test_generating_v2_message() {
429        let message = rumtk_v2_parse_message!(&DEFAULT_HL7_V2_MESSAGE).unwrap();
430        let generated_message_string = rumtk_v2_generate_message!(&message);
431        let generated_message = rumtk_v2_parse_message!(&generated_message_string).unwrap();
432        assert_eq!(
433            &message, &generated_message,
434            "Messages are not equal! Expected: {:?} Got: {:?}",
435            &message, &generated_message
436        );
437    }
438
439    #[test]
440    fn test_generating_v2_message_wir() {
441        let message = rumtk_v2_parse_message!(&HL7_V2_MESSAGE).unwrap();
442        let generated_message_string = rumtk_v2_generate_message!(&message);
443        let generated_message = rumtk_v2_parse_message!(&generated_message_string).unwrap();
444        assert_eq!(
445            &message, &generated_message,
446            "Messages are not equal! Expected: {:?} Got: {:?}",
447            &message, &generated_message
448        );
449    }
450
451    #[test]
452    fn test_generating_v2_message_pdf() {
453        let message = rumtk_v2_parse_message!(&HL7_V2_PDF_MESSAGE).unwrap();
454        let generated_message_string = rumtk_v2_generate_message!(&message);
455        let generated_message = rumtk_v2_parse_message!(&generated_message_string).unwrap();
456        assert_eq!(
457            &message, &generated_message,
458            "Messages are not equal! Expected: {:?} Got: {:?}",
459            &message, &generated_message
460        );
461    }
462
463    #[test]
464    fn test_generating_v2_message_repeated_fields() {
465        let message = rumtk_v2_parse_message!(&HL7_V2_REPEATING_FIELD_MESSAGE).unwrap();
466        let generated_message_string = rumtk_v2_generate_message!(&message);
467        let generated_message = rumtk_v2_parse_message!(&generated_message_string).unwrap();
468        assert_eq!(
469            &message, &generated_message,
470            "Messages are not equal! Expected: {:?} Got: {:?}",
471            &message, &generated_message
472        );
473    }
474
475    #[test]
476    fn test_handle_hl7_v2_search_pattern_parsing_full() {
477        let pattern = "MSH(1)-1[5].4";
478        let groups = string_search_named_captures(pattern, REGEX_V2_SEARCH_DEFAULT, "1");
479        let expected = SearchGroups::from([
480            (RUMString::new("segment_group"), RUMString::new("1")),
481            (RUMString::new("sub_field"), RUMString::new("5")),
482            (RUMString::new("segment"), RUMString::new("MSH")),
483            (RUMString::new("field"), RUMString::new("-1")),
484            (RUMString::new("component"), RUMString::new("4")),
485        ]);
486        println!(
487            "Input: {:?} Expected: {:?} Got: {:?}",
488            pattern, expected, groups
489        );
490        assert_eq!(
491            groups, expected,
492            "Misparsed search expression MSH(1)-1[5].4!"
493        );
494    }
495
496    #[test]
497    fn test_handle_hl7_v2_search_pattern_parsing_simple() {
498        let pattern = "MSH1.4";
499        let groups = string_search_named_captures(pattern, REGEX_V2_SEARCH_DEFAULT, "1");
500        let expected = SearchGroups::from([
501            (RUMString::new("segment_group"), RUMString::new("1")),
502            (RUMString::new("sub_field"), RUMString::new("1")),
503            (RUMString::new("segment"), RUMString::new("MSH")),
504            (RUMString::new("field"), RUMString::new("1")),
505            (RUMString::new("component"), RUMString::new("4")),
506        ]);
507        println!(
508            "Input: {:?} Expected: {:?} Got: {:?}",
509            pattern, expected, groups
510        );
511        assert_eq!(groups, expected, "Misparsed search expression MSH1.4!");
512    }
513
514    #[test]
515    fn test_v2_search_index() {
516        let expr = "MSH(1)-1[5].4";
517        let v2_search_index = V2SearchIndex::from(expr);
518        let expected = V2SearchIndex::new("MSH", 1, -1, 5, 4);
519        println!(
520            "Input: {:?} Expected: {:?} Got: {:?}",
521            expr, expected, v2_search_index
522        );
523        assert_eq!(
524            v2_search_index, expected,
525            "Failed to parse expression into correct SearchIndex object."
526        );
527    }
528
529    #[test]
530    fn test_load_hl7_v2_message_macro() {
531        let message = rumtk_v2_parse_message!(DEFAULT_HL7_V2_MESSAGE).unwrap();
532        assert!(
533            message.segment_exists(&V2_SEGMENT_IDS["MSH"]),
534            "Missing MSH segment!"
535        );
536        assert!(
537            message.segment_exists(&V2_SEGMENT_IDS["PID"]),
538            "Missing PID segment!"
539        );
540        assert!(
541            message.segment_exists(&V2_SEGMENT_IDS["PV1"]),
542            "Missing PV1 segment!"
543        );
544        assert!(
545            message.segment_exists(&V2_SEGMENT_IDS["EVN"]),
546            "Missing EVN segment!"
547        );
548        assert!(
549            message.segment_exists(&V2_SEGMENT_IDS["NK1"]),
550            "Missing NK1 segment!"
551        );
552    }
553
554    #[test]
555    fn test_load_hl7_v2_message_macro_failure() {
556        let input = "Hello World!";
557        let err_msg = rumtk_format!(
558            "Parsing did not fail as expected. Input {} => parsed?",
559            input
560        );
561        match rumtk_v2_parse_message!(input) {
562            Ok(v) => panic!("{}", err_msg.as_str()),
563            Err(e) => {
564                println!("{}", rumtk_format!("Got error => {}", e).as_str());
565                println!("Passed failed case!");
566            }
567        };
568    }
569
570    #[test]
571    fn test_find_hl7_v2_message_component_macro() {
572        let pattern = "PID(1)5.4";
573        let message = rumtk_v2_parse_message!(DEFAULT_HL7_V2_MESSAGE).unwrap();
574        let component = rumtk_v2_find_component!(message, pattern).unwrap();
575        let expected = "III";
576        assert_eq!(
577            component.as_str(),
578            expected,
579            "Wrong component found! Looked for {} expecting {}, but got {}",
580            pattern,
581            expected,
582            component.as_str()
583        );
584    }
585
586    #[test]
587    fn test_find_hl7_v2_message_component_simple_macro() {
588        let pattern = "PID5.4";
589        let message = rumtk_v2_parse_message!(DEFAULT_HL7_V2_MESSAGE).unwrap();
590        let component = rumtk_v2_find_component!(message, pattern).unwrap();
591        let expected = "III";
592        assert_eq!(
593            component.as_str(),
594            expected,
595            "Wrong component found! Looked for {} expecting {}, but got {}",
596            pattern,
597            expected,
598            component.as_str()
599        );
600    }
601
602    #[test]
603    fn test_find_hl7_v2_message_msh_field() {
604        let pattern = "MSH1.1";
605        let message = rumtk_v2_parse_message!(HL7_V2_MSH_ONLY).unwrap();
606        let component = rumtk_v2_find_component!(message, pattern).unwrap();
607        let expected = "^~\\&";
608        assert_eq!(
609            component.as_str(),
610            expected,
611            "Wrong component found! Looked for {} expecting {}, but got {}",
612            pattern,
613            expected,
614            component.as_str()
615        );
616    }
617
618    #[test]
619    fn test_find_hl7_v2_message_component_macro_failure() {
620        let pattern = "PID(1)15.4";
621        let err_msg = rumtk_format!(
622            "Search did not fail as expected. Input {} => found component?",
623            pattern
624        );
625        let message = rumtk_v2_parse_message!(DEFAULT_HL7_V2_MESSAGE).unwrap();
626        match rumtk_v2_find_component!(message, pattern) {
627            Ok(v) => panic!("{}", err_msg.as_str()),
628            Err(e) => {
629                println!("{}", rumtk_format!("Got error => {}", e).as_str());
630                println!("Passed failed case!");
631            }
632        }
633    }
634
635    #[test]
636    fn test_cast_component_to_datetime_expected_functionality() {
637        let inputs = [
638            "2007",
639            "200708",
640            "20070818",
641            "200708181123",
642            "20070818112355",
643            "20070818112355.55",
644            "20070818112355.5555-5000",
645            "20070818112355-5000",
646        ];
647        let expected_outputs = [
648            "2007-01-01T00:00:00.0000",
649            "2007-08-01T00:00:00.0000",
650            "2007-08-18T00:00:00.0000",
651            "2007-08-18T11:23:00.0000",
652            "2007-08-18T11:23:55.0000",
653            "2007-08-18T11:23:55.5500",
654            "2007-08-18T11:23:55.5555-5000",
655            "2007-08-18T11:23:55.0000-5000",
656        ];
657        for i in 0..inputs.len() {
658            let input = inputs[i];
659            let expected_utc = expected_outputs[i];
660            print!(
661                "Testing input #{} \"{}\". Expected output is \"{}\". Casting to datetime type.",
662                i, input, expected_utc
663            );
664            let date = input.to_v2datetime().unwrap();
665            let err_msg = rumtk_format!("The expected date time string does not match the date time string generated from the input [In: {}, Got: {}]", input, date.as_utc_string());
666            assert_eq!(expected_utc, date.as_utc_string().as_str(), "{}", &err_msg);
667            println!(" ... Got: {} ✅ ", date.as_utc_string());
668        }
669    }
670
671    #[test]
672    fn test_cast_component_to_datetime_validation() {
673        let inputs = ["200"];
674        for input in inputs {
675            match input.to_v2datetime() {
676                Ok(date) => {
677                    panic!(
678                        "Validation failed [In: {} Got: {} Expected: None] ... ✕",
679                        input,
680                        date.as_utc_string()
681                    );
682                }
683                Err(e) => println!(
684                    "Validation correctly identified malformed input with message => [{}] ✅",
685                    e.as_str()
686                ),
687            }
688        }
689    }
690
691    #[test]
692    fn test_cast_component_to_datetime_base_example() {
693        let location = "EVN2"; //EVN|A01|200708181123||\n\r; EVN2 => segment = EVN, field = 2
694        let expected_component = "200708181123";
695        let message = rumtk_v2_parse_message!(DEFAULT_HL7_V2_MESSAGE).unwrap();
696        let component = rumtk_v2_find_component!(message, location).unwrap();
697        assert_eq!(expected_component, component.as_str(), "We are not using the correct component for this test. Check that the original test message has not changed and update the location string appropriately!");
698        let date = component.to_v2datetime().unwrap();
699        let expected_utc = "2007-08-18T11:23:00.0000";
700        let err_msg = rumtk_format!("The expected date time string does not match the date time string generated from the input [{}]", component.as_str());
701        assert_eq!(expected_utc, date.as_utc_string().as_str(), "{}", &err_msg)
702    }
703
704    #[test]
705    fn test_datetime_default() {
706        let input = V2DateTime::default().as_utc_string();
707        let expected_val = V2String::from("1970-01-01T00:00:00.00000");
708        let err_msg = rumtk_format!("The expected formatted string does not match the formatted string generated from the input [In: {}, Got: {}]", input, input);
709        assert_eq!(expected_val, input, "{}", &err_msg);
710    }
711
712    #[test]
713    fn test_cast_component_to_date_expected_functionality() {
714        let inputs = ["2007", "200708", "20070818"];
715        let expected_outputs = [
716            "2007-01-01T00:00:00.0000",
717            "2007-08-01T00:00:00.0000",
718            "2007-08-18T00:00:00.0000",
719        ];
720        for i in 0..inputs.len() {
721            let input = inputs[i];
722            let expected_utc = expected_outputs[i];
723            print!(
724                "Testing input #{} \"{}\". Expected output is \"{}\". Casting to datetime type.",
725                i, input, expected_utc
726            );
727            let date = input.to_v2date().unwrap();
728            let err_msg = rumtk_format!("The expected date time string does not match the date time string generated from the input [In: {}, Got: {}]", input, date.as_utc_string());
729            assert_eq!(expected_utc, date.as_utc_string().as_str(), "{}", &err_msg);
730            println!(" ... Got: {} ✅ ", date.as_utc_string());
731        }
732    }
733
734    #[test]
735    fn test_cast_component_to_date_validation() {
736        let inputs = ["200"];
737        for input in inputs {
738            match input.to_v2date() {
739                Ok(date) => {
740                    panic!(
741                        "Validation failed [In: {} Got: {} Expected: None] ... ✕",
742                        input,
743                        date.as_utc_string()
744                    );
745                }
746                Err(e) => println!(
747                    "Validation correctly identified malformed input with message => [{}] ✅",
748                    e.as_str()
749                ),
750            }
751        }
752    }
753
754    #[test]
755    fn test_cast_component_to_date_base_example() {
756        let location = "PD113"; //EVN|A01|200708181123||\n\r; PD113 => segment = PD1, field = 13
757        let expected_component = "20150625";
758        let message = rumtk_v2_parse_message!(VXU_HL7_V2_MESSAGE).unwrap();
759        let component = rumtk_v2_find_component!(message, location).unwrap();
760        assert_eq!(expected_component, component.as_str(), "We are not using the correct component for this test. Check that the original test message has not changed and update the location string appropriately!");
761        let date = component.to_v2date().unwrap();
762        let expected_utc = "2015-06-25T00:00:00.0000";
763        let err_msg = rumtk_format!(
764            "The expected date string does not match the date string generated from the input [{}]",
765            component.as_str()
766        );
767        assert_eq!(expected_utc, date.as_utc_string().as_str(), "{}", &err_msg)
768    }
769
770    #[test]
771    fn test_cast_component_to_time_expected_functionality() {
772        let inputs = ["1123", "112355", "112355.5555", "112355.5555-5000"];
773        let expected_outputs = [
774            "1970-01-01T11:23:00.0000",
775            "1970-01-01T11:23:55.0000",
776            "1970-01-01T11:23:55.5555",
777            "1970-01-01T11:23:55.5555-5000",
778        ];
779        for i in 0..inputs.len() {
780            let input = inputs[i];
781            let expected_utc = expected_outputs[i];
782            print!(
783                "Testing input #{} \"{}\". Expected output is \"{}\". Casting to datetime type.",
784                i, input, expected_utc
785            );
786            let date = input.to_v2time().unwrap();
787            let err_msg = rumtk_format!("The expected date time string does not match the date time string generated from the input [In: {}, Got: {}]", input, date.as_utc_string());
788            assert_eq!(expected_utc, date.as_utc_string().as_str(), "{}", &err_msg);
789            println!(" ... Got: {} ✅ ", date.as_utc_string());
790        }
791    }
792
793    #[test]
794    fn test_cast_component_to_time_validation() {
795        let inputs = ["2"];
796        for input in inputs {
797            match input.to_v2time() {
798                Ok(date) => {
799                    panic!(
800                        "Validation failed [In: {} Got: {} Expected: None] ... ✕",
801                        input,
802                        date.as_utc_string()
803                    );
804                }
805                Err(e) => println!(
806                    "Validation correctly identified malformed input with message => [{}] ✅",
807                    e.as_str()
808                ),
809            }
810        }
811    }
812
813    #[test]
814    fn test_cast_component_to_number_expected_functionality() {
815        let inputs = [
816            "5e3",
817            "5E3",
818            "112355.5555",
819            "5F",
820            "5.5F",
821            "5f",
822            "5.5e2",
823            "-5f",
824            "-05e1",
825        ];
826        let expected_outputs = [
827            5000.0,
828            5000.0,
829            112355.5555,
830            5.0,
831            5.5,
832            5.0,
833            550.0,
834            -5.0,
835            -50.0,
836        ];
837        for i in 0..inputs.len() {
838            let input = inputs[i];
839            let expected_val = expected_outputs[i];
840            print!(
841                "Testing input #{} \"{}\". Expected output is \"{}\". Casting to NM type.",
842                i, input, expected_val
843            );
844            let val = input.to_v2number().unwrap();
845            let err_msg = rumtk_format!("The expected date time string does not match the date time string generated from the input [In: {}, Got: {}]", input, val);
846            assert_eq!(expected_val, val, "{}", &err_msg);
847            println!(" ... Got: {} ✅ ", val);
848        }
849    }
850
851    #[test]
852    fn test_cast_component_to_number_validation() {
853        let inputs = [".2"];
854        for input in inputs {
855            match input.to_v2number() {
856                Ok(val) => {
857                    panic!(
858                        "Validation failed [In: {} Got: {} Expected: None] ... ✕",
859                        input, val
860                    );
861                }
862                Err(e) => println!(
863                    "Validation correctly identified malformed input with message => [{}] ✅",
864                    e.as_str()
865                ),
866            }
867        }
868    }
869
870    #[test]
871    fn test_cast_component_to_st_expected_functionality() {
872        let inputs = [" Hello World!"];
873        let expected_outputs = ["Hello World!"];
874        for i in 0..inputs.len() {
875            let input = inputs[i];
876            let expected_val = expected_outputs[i];
877            print!(
878                "Testing input #{} \"{}\". Expected output is \"{}\". Casting to ST type.",
879                i, input, expected_val
880            );
881            let val = input.to_v2stringdata().unwrap();
882            let err_msg = rumtk_format!("The expected date time string does not match the date time string generated from the input [In: {}, Got: {}]", input, val);
883            assert_eq!(expected_val, val, "{}", &err_msg);
884            println!(" ... Got: {} ✅ ", val);
885        }
886    }
887
888    #[test]
889    fn test_cast_component_to_st_validation() {
890        let input = "2".duplicate(1001);
891        println!("{}", input);
892        match input.to_v2stringdata() {
893            Ok(val) => {
894                panic!(
895                    "Validation failed [In: {} Got: {} Expected: None] ... ✕",
896                    input, val
897                );
898            }
899            Err(e) => println!(
900                "Validation correctly identified malformed input with message => [{}] ✅",
901                e.as_str()
902            ),
903        }
904    }
905
906    #[test]
907    fn test_cast_component_to_ft_expected_functionality() {
908        let inputs = ["H", &"e".duplicate(120000)];
909        let expected_outputs = ["H", &"e".duplicate(TRUNCATE_FT as usize)];
910        for i in 0..inputs.len() {
911            let input = inputs[i];
912            let expected_val = expected_outputs[i];
913            print!(
914                "Testing input #{} \"{}\". Expected output is \"{}\". Casting to FT type.",
915                i, input, expected_val
916            );
917            let val = input.to_v2formattedtext("~").unwrap();
918            println!("{}", val.len());
919            let err_msg = rumtk_format!("The expected formatted string does not match the formatted string generated from the input [In: {}, Got: {}]", input, val);
920            assert_eq!(expected_val, val, "{}", &err_msg);
921            println!(" ... Got: {} ✅ ", val);
922        }
923    }
924
925    #[test]
926    fn test_validated_cast_component_to_type() {
927        let message = DEFAULT_HL7_V2_MESSAGE;
928        let sanitized_message = V2Message::sanitize(message);
929        let tokens = V2Message::tokenize_segments(&sanitized_message.as_str());
930        let encode_chars = V2ParserCharacters::from_msh(tokens[0]).unwrap();
931        let v2_component = V2ComponentTypeDescriptor::new(
932            "date",
933            "Date",
934            V2ComponentType::Primitive(V2PrimitiveType::Date),
935            4,
936            1,
937            1,
938            Optionality::O,
939            true,
940        );
941        let input = "2007";
942        let val = cast_component(vec![&input], &v2_component, &encode_chars);
943        let expected = "2007-01-01T00:00:00.0000";
944        let err_msg = rumtk_format!("The expected formatted string does not match the formatted string generated from the input [In: {}, Got: {}]", input, expected);
945
946        match val {
947            V2Type::V2Date(result) => {
948                assert_eq!(expected, result.unwrap().as_utc_string(), "{}", &err_msg)
949            }
950            _ => panic!("Wrong type received!"),
951        }
952    }
953
954    // TODO: Add tests for sequenceid and telephonestring
955    // TODO: Add fuzzing test for to_datetime().
956
957    #[test]
958    fn test_mllp_encode() {
959        let expected_message = RUMString::from("I ❤ my wife!");
960        let encoded = mllp_encode(&expected_message);
961        let payload = &encoded[1..encoded.len() - 2];
962
963        assert_eq!(encoded[0], SB, "Incorrect start byte in MLLP message!");
964
965        assert_eq!(
966            encoded[encoded.len() - 2],
967            EB,
968            "Incorrect end byte in MLLP message!"
969        );
970
971        assert_eq!(
972            encoded[encoded.len() - 1],
973            CR,
974            "Missing mandatory carriage return in MLLP message!"
975        );
976
977        assert_eq!(
978            expected_message,
979            payload.to_rumstring(),
980            "{}",
981            rumtk_format!(
982                "Malformed payload! Expected: {} Found: {}",
983                expected_message,
984                payload.to_rumstring()
985            )
986        );
987    }
988
989    #[test]
990    fn test_mllp_decode() {
991        let expected_message = RUMString::from("I ❤ my wife!");
992        let message_size = expected_message.len();
993        let encoded = mllp_encode(&expected_message);
994        let encoded_size = encoded.len();
995
996        assert_eq!(
997            encoded_size,
998            message_size + 3,
999            "Incorrect encoded message size!"
1000        );
1001
1002        let decoded = mllp_decode(&encoded).unwrap();
1003        let decoded_size = decoded.len();
1004
1005        assert_eq!(
1006            decoded_size, message_size,
1007            "Incorrect decoded message size! Expected: {} Got: {}",
1008            expected_message, decoded
1009        );
1010
1011        assert_eq!(
1012            expected_message,
1013            decoded,
1014            "{}",
1015            rumtk_format!(
1016                "Malformed decoded message! Expected: {} Found: {}",
1017                expected_message,
1018                decoded
1019            )
1020        );
1021    }
1022
1023    #[test]
1024    fn test_mllp_listen() {
1025        let mllp_layer = match rumtk_v2_mllp_listen!(0, MLLP_FILTER_POLICY::NONE, true) {
1026            Ok(mllp_layer) => mllp_layer,
1027            Err(e) => panic!("{}", e),
1028        };
1029        let (ip, port) = rumtk_v2_mllp_get_ip_port!(&mllp_layer);
1030        let client_id = rumtk_exec_task!(async || -> RUMResult<RUMString> {
1031            Ok(mllp_layer.lock().await.get_address_info().await.unwrap())
1032        });
1033        assert_eq!(
1034            client_id,
1035            Ok(rumtk_format!("127.0.0.1:{}", &port)),
1036            "Failed to bind local port!"
1037        )
1038    }
1039
1040    #[test]
1041    fn test_mllp_get_ip() {
1042        let mllp_layer = match rumtk_v2_mllp_listen!(0, MLLP_FILTER_POLICY::NONE, true) {
1043            Ok(mllp_layer) => mllp_layer,
1044            Err(e) => panic!("{}", e),
1045        };
1046        let (ip, port) = rumtk_v2_mllp_get_ip_port!(&mllp_layer);
1047    }
1048
1049    #[test]
1050    fn test_mllp_connect() {
1051        let mllp_layer = match rumtk_v2_mllp_listen!(0, MLLP_FILTER_POLICY::NONE, true) {
1052            Ok(mllp_layer) => mllp_layer,
1053            Err(e) => panic!("{}", e),
1054        };
1055        let (ip, port) = rumtk_v2_mllp_get_ip_port!(&mllp_layer);
1056        let client = match rumtk_v2_mllp_connect!(port, MLLP_FILTER_POLICY::NONE) {
1057            Ok(client) => client,
1058            Err(e) => panic!("{}", e),
1059        };
1060        rumtk_sleep!(1);
1061        let mut connected_clients = rumtk_v2_mllp_get_client_ids!(&mllp_layer);
1062        for i in 0..10 {
1063            if connected_clients.is_empty() {
1064                rumtk_sleep!(1);
1065                connected_clients = rumtk_v2_mllp_get_client_ids!(&mllp_layer);
1066            }
1067        }
1068        let connected_address = connected_clients.get(0).unwrap();
1069        let client_ids = rumtk_v2_mllp_get_client_ids!(&client);
1070        let client_id = client_ids.get(0).unwrap();
1071        assert_eq!(connected_address, client_id, "Failed to bind local port!")
1072    }
1073
1074    #[test]
1075    fn test_mllp_channel() {
1076        let empty_string = |s: RUMString| Ok::<RUMString, RUMString>(RUMString::from(""));
1077        let safe_listener = match rumtk_v2_mllp_listen!(0, MLLP_FILTER_POLICY::NONE, true) {
1078            Ok(mllp_layer) => mllp_layer,
1079            Err(e) => panic!("{}", e),
1080        };
1081        let (ip, port) = rumtk_v2_mllp_get_ip_port!(&safe_listener);
1082        let safe_client = match rumtk_v2_mllp_connect!(port, MLLP_FILTER_POLICY::NONE) {
1083            Ok(client) => client,
1084            Err(e) => panic!("{}", e),
1085        };
1086        rumtk_sleep!(1);
1087        let client_ids = rumtk_v2_mllp_get_client_ids!(&safe_listener);
1088        let client_id = client_ids.get(0).unwrap();
1089        let mut server_channels = rumtk_v2_mllp_iter_channels!(&safe_client);
1090        let mut server_channel = server_channels.get_mut(0).unwrap().clone();
1091        let channel_address = server_channel.lock().unwrap().get_address_info().unwrap();
1092        assert_eq!(
1093            &client_id,
1094            &channel_address,
1095            "{}",
1096            rumtk_format!(
1097                "Issue stablishing MLLP communication channel! Expected: {} Received: {}",
1098                &client_id,
1099                &channel_address
1100            )
1101        )
1102    }
1103
1104    #[test]
1105    fn test_mllp_channel_async_communication() {
1106        let mut safe_listener = match rumtk_v2_mllp_listen!(0, MLLP_FILTER_POLICY::NONE, true) {
1107            Ok(mllp_layer) => mllp_layer,
1108            Err(e) => panic!("{}", e),
1109        };
1110        let (ip, port) = rumtk_v2_mllp_get_ip_port!(&safe_listener);
1111        let safe_client = match rumtk_v2_mllp_connect!(port, MLLP_FILTER_POLICY::NONE) {
1112            Ok(client) => client,
1113            Err(e) => panic!("{}", e),
1114        };
1115        rumtk_sleep!(1);
1116        let client_ids = rumtk_v2_mllp_get_client_ids!(&safe_listener);
1117        let client_id = client_ids.get(0).unwrap();
1118        let mut server_channels = rumtk_v2_mllp_iter_channels!(&safe_client);
1119        let mut server_channel = server_channels.get_mut(0).unwrap().clone();
1120        let expected_message = RUMString::from("I ❤ my wife!");
1121        let message_copy = expected_message.clone();
1122        let send_thread = spawn(move || -> RUMResult<()> {
1123            Ok(server_channel
1124                .lock()
1125                .unwrap()
1126                .send_message(&message_copy)
1127                .unwrap())
1128        });
1129        rumtk_sleep!(1);
1130        let received_message = rumtk_exec_task!(async || -> RUMResult<RUMString> {
1131            let mut received_message = safe_listener
1132                .lock()
1133                .await
1134                .receive_message(&client_id)
1135                .await?;
1136            while received_message.len() == 0 {
1137                received_message = safe_listener
1138                    .lock()
1139                    .await
1140                    .receive_message(&client_id)
1141                    .await?;
1142            }
1143            Ok(received_message)
1144        })
1145        .unwrap();
1146        assert_eq!(
1147            &expected_message,
1148            &received_message,
1149            "{}",
1150            rumtk_format!(
1151                "Issue sending message through channel! Expected: {} Received: {}",
1152                &expected_message,
1153                &received_message
1154            )
1155        )
1156    }
1157
1158    #[test]
1159    fn test_mllp_hl7_echo() {
1160        let empty_string = |s: RUMString| Ok::<RUMString, RUMString>(RUMString::from(""));
1161        let mut safe_listener = match rumtk_v2_mllp_listen!(0, MLLP_FILTER_POLICY::NONE, true) {
1162            Ok(mllp_listener) => mllp_listener,
1163            Err(e) => panic!("{}", e),
1164        };
1165        let (ip, port) = rumtk_v2_mllp_get_ip_port!(&safe_listener);
1166        let safe_client = match rumtk_v2_mllp_connect!(port, MLLP_FILTER_POLICY::NONE) {
1167            Ok(client) => client,
1168            Err(e) => panic!("{}", e),
1169        };
1170        rumtk_sleep!(1);
1171        let client_ids = rumtk_v2_mllp_get_client_ids!(&safe_listener);
1172        let client_id = client_ids.get(0).unwrap();
1173        let mut server_channels = rumtk_v2_mllp_iter_channels!(&safe_client);
1174        let mut server_channel = server_channels.get_mut(0).unwrap().clone();
1175        let server_channel_copy = server_channel.clone();
1176        let send_thread = spawn(move || -> RUMResult<()> {
1177            Ok(server_channel
1178                .lock()
1179                .unwrap()
1180                .send_message(HL7_V2_PDF_MESSAGE)
1181                .unwrap())
1182        });
1183        let safe_listener_copy = safe_listener.clone();
1184        let received_message = rumtk_exec_task!(async || -> RUMResult<RUMString> {
1185            let mut received_message = safe_listener_copy
1186                .lock()
1187                .await
1188                .receive_message(&client_id)
1189                .await?;
1190            while received_message.len() == 0 {
1191                received_message = safe_listener_copy
1192                    .lock()
1193                    .await
1194                    .receive_message(&client_id)
1195                    .await?;
1196            }
1197            Ok(received_message)
1198        })
1199        .unwrap();
1200        assert_eq!(
1201            &HL7_V2_PDF_MESSAGE,
1202            &received_message,
1203            "{}",
1204            rumtk_format!(
1205                "Issue sending message through channel! Expected: {} Received: {}",
1206                &HL7_V2_PDF_MESSAGE,
1207                &received_message
1208            )
1209        );
1210        let client_id_copy = client_id.clone();
1211        let safe_listener_copy2 = safe_listener.clone();
1212        println!("Echoing message back to client!");
1213        let echo_thread = spawn(move || {
1214            println!("Sending echo message!");
1215            rumtk_v2_mllp_send!(safe_listener_copy2, client_id_copy, HL7_V2_PDF_MESSAGE).unwrap();
1216            println!("Sent echo message!");
1217        });
1218        rumtk_sleep!(1);
1219        let echoed_message = rumtk_exec_task!(async || -> RUMResult<RUMString> {
1220            println!("Echoing message back to client!");
1221            let mut echoed_message = safe_client.lock().await.receive_message(&client_id).await?;
1222            while echoed_message.len() == 0 {
1223                echoed_message = safe_client.lock().await.receive_message(&client_id).await?;
1224            }
1225            println!("Echoed message: {}", &echoed_message);
1226            Ok(echoed_message)
1227        })
1228        .unwrap();
1229        assert_eq!(
1230            &HL7_V2_PDF_MESSAGE,
1231            &echoed_message,
1232            "{}",
1233            rumtk_format!(
1234                "Issue echoing message through channel! Expected: {} Received: {}",
1235                &HL7_V2_PDF_MESSAGE,
1236                &echoed_message
1237            )
1238        )
1239    }
1240
1241    ////////////////////////////JSON Tests/////////////////////////////////
1242    #[test]
1243    fn test_deserialize_escaped_v2_message() {
1244        let message = rumtk_v2_parse_message!(V2_JSON_MESSAGE).unwrap();
1245        let serialized = rumtk_serialize!(&message).unwrap();
1246        let escaped = basic_escape(&serialized, &vec![]);
1247        let deserialized = rumtk_deserialize!(&escaped).unwrap();
1248
1249        assert_eq!(
1250            message, deserialized,
1251            "Deserialized JSON does not match the expected value!"
1252        );
1253    }
1254
1255    #[test]
1256    fn test_deserialize_v2_message() {
1257        let message = rumtk_v2_parse_message!(DEFAULT_HL7_V2_MESSAGE).unwrap();
1258        let message_str = rumtk_serialize!(&message, true).unwrap();
1259        let deserialized: V2Message = rumtk_deserialize!(&message_str).unwrap();
1260
1261        assert_eq!(
1262            message, deserialized,
1263            "Deserialized JSON does not match the expected value!"
1264        );
1265    }
1266
1267    #[test]
1268    fn test_deserialize_stdin_v2_message_basic() {
1269        let expected_message: V2Message = rumtk_v2_parse_message!(V2_JSON_MESSAGE_BASIC).unwrap();
1270        let deserialized: V2Message = rumtk_deserialize!(&ESCAPED_V2_JSON_MESSAGE_BASIC).unwrap();
1271
1272        assert_eq!(
1273            expected_message, deserialized,
1274            "Deserialized Escaped JSON does not match the expected value!"
1275        );
1276    }
1277
1278    ////////////////////////////Fuzzed Tests/////////////////////////////////
1279
1280    #[test]
1281    fn test_fuzzed_garbage_parsing() {
1282        let input = "MSH@~��MS";
1283        match rumtk_v2_parse_message!(&input) {
1284            Err(e) => println!("Correctly identified input as garbage! => {}", &e),
1285            Ok(message) => {
1286                println!("Test input [{}] Result => {:?}", &input, message);
1287                panic!("Message parsed without errors despite being malformed!")
1288            }
1289        }
1290    }
1291}