use super::segments::Segment;
use super::separators::Separators;
use super::*;
use std::convert::TryFrom;
use std::fmt::Display;
use std::ops::Index;
#[derive(Debug, PartialEq)]
pub struct Message<'a> {
pub source: &'a str,
pub segments: Vec<Segment<'a>>,
separators: Separators,
}
impl<'a> Message<'a> {
pub fn new(source: &'a str) -> Message<'a> {
let separators = str::parse::<Separators>(source).unwrap();
let segments: Vec<Segment<'a>> = source
.split(separators.segment)
.map(|line| Segment::parse(line, &separators).unwrap())
.collect();
Message {
source,
segments,
separators,
}
}
pub fn segments_by_name(&self, name: &str) -> Result<Vec<&Segment<'a>>, Hl7ParseError> {
let found: Vec<&Segment<'a>> = self
.segments
.iter()
.filter(|s| s.fields[0].source == name)
.collect();
Ok(found)
}
pub fn segments_to_str_vecs(
segments: Vec<&Segment<'a>>,
) -> Result<Vec<Vec<&'a str>>, Hl7ParseError> {
let vecs = segments
.iter()
.map(|s| s.fields.iter().map(|f| f.value()).collect())
.collect();
Ok(vecs)
}
#[inline]
pub fn as_str(&self) -> &'a str {
self.source
}
pub fn get_separators(&self) -> Separators {
self.separators
}
pub fn query<'b, S>(&self, idx: S) -> &'a str
where
S: Into<&'b str>,
{
let idx = idx.into();
let indices = Self::parse_query_string(idx);
let seg_name = indices[0];
let seg_index = self
.segments
.iter()
.position(|r| &r.as_str()[..seg_name.len()] == seg_name)
.expect("Segment not found");
let seg = &self.segments[seg_index];
if indices.len() < 2 {
seg.source
} else {
let query = indices[1..].join(".");
seg.query(&*query)
}
}
pub fn parse_query_string(query: &str) -> Vec<&str> {
fn query_idx_pos(indices: &[&str], idx: &str) -> Option<usize> {
indices[1..]
.iter()
.position(|r| r[0..1].to_uppercase() == idx)
}
let indices: Vec<&str> = query.split('.').collect();
let mut res = vec![indices[0]];
let sub_pos = query_idx_pos(&indices, "S");
let com_pos = query_idx_pos(&indices, "C");
let rep_pos = query_idx_pos(&indices, "R");
let fld_pos = query_idx_pos(&indices, "F");
match fld_pos {
Some(f) => res.push(indices[f + 1]),
None => {
if rep_pos.is_some() || com_pos.is_some() || sub_pos.is_some() {
res.push("F1")
} else {
return res;
}
}
};
match rep_pos {
Some(r) => res.push(indices[r + 1]),
None => {
if com_pos.is_some() || sub_pos.is_some() {
res.push("R1")
} else {
return res;
}
}
};
match com_pos {
Some(c) => res.push(indices[c + 1]),
None => {
if sub_pos.is_some() {
res.push("C1")
} else {
return res;
}
}
};
if let Some(s) = sub_pos {
res.push(indices[s + 1])
}
res
}
}
impl<'a> TryFrom<&'a str> for Message<'a> {
type Error = Hl7ParseError;
fn try_from(source: &'a str) -> Result<Self, Self::Error> {
let delimiters = str::parse::<Separators>(source)?;
let segments: Result<Vec<Segment<'a>>, Hl7ParseError> = source
.split(delimiters.segment)
.map(|line| Segment::parse(line, &delimiters))
.collect();
let msg = Message {
source,
segments: segments?,
separators: delimiters,
};
Ok(msg)
}
}
impl<'a> Display for Message<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.source)
}
}
impl<'a> Clone for Message<'a> {
fn clone(&self) -> Self {
Message::try_from(self.source).unwrap()
}
}
impl<'a> Index<usize> for Message<'a> {
type Output = &'a str;
fn index(&self, idx: usize) -> &Self::Output {
if idx > self.segments.len() {
return &"";
}
&self.segments[idx].source
}
}
#[cfg(feature = "string_index")]
impl<'a> Index<String> for Message<'a> {
type Output = &'a str;
#[cfg(feature = "string_index")]
fn index(&self, idx: String) -> &Self::Output {
let indices = Self::parse_query_string(&idx);
let seg_name = indices[0];
let seg_index = self
.segments
.iter()
.position(|r| &r.as_str()[..seg_name.len()] == seg_name)
.expect("Segment not found");
let seg = &self.segments[seg_index];
if indices.len() < 2 {
&seg.source
} else {
&seg[indices[1..].join(".")]
}
}
}
#[cfg(feature = "string_index")]
impl<'a> Index<&str> for Message<'a> {
type Output = &'a str;
#[cfg(feature = "string_index")]
fn index(&self, idx: &str) -> &Self::Output {
&self[String::from(idx)]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ensure_segments_are_returned() -> Result<(), Hl7ParseError> {
let hl7 = "MSH|^~\\&|GHH LAB|ELAB-3|GHH OE|BLDG4|200202150930||ORU^R01|CNTRL-3456|P|2.4\rOBR|segment";
let msg = Message::try_from(hl7)?;
assert_eq!(msg.segments.len(), 2);
Ok(())
}
#[test]
fn ensure_segments_are_found() -> Result<(), Hl7ParseError> {
let hl7 = "MSH|^~\\&|GHH LAB|ELAB-3|GHH OE|BLDG4|200202150930||ORU^R01|CNTRL-3456|P|2.4\rOBR|segment";
let msg = Message::try_from(hl7)?;
assert_eq!(msg.segments_by_name("OBR").unwrap().len(), 1);
Ok(())
}
#[test]
fn ensure_segments_convert_to_vectors() -> Result<(), Hl7ParseError> {
let hl7 = "MSH|^~\\&|GHH LAB|ELAB-3|GHH OE|BLDG4|200202150930||ORU^R01|CNTRL-3456|P|2.4\rOBR|segment";
let msg = Message::try_from(hl7)?;
let segs = msg.segments_by_name("OBR")?;
let sval = segs.first().unwrap().fields.first().unwrap().value();
let vecs = Message::segments_to_str_vecs(segs).unwrap();
let vval = vecs.first().unwrap().first().unwrap();
assert_eq!(vval, &sval);
Ok(())
}
#[test]
fn ensure_clones_are_owned() -> Result<(), Hl7ParseError> {
let hl7 = "MSH|^~\\&|GHH LAB|ELAB-3|GHH OE|BLDG4|200202150930||ORU^R01|CNTRL-3456|P|2.4\rOBR|segment";
let msg = Message::try_from(hl7)?;
let dolly = msg.clone();
let dolly = dolly.to_owned();
assert_eq!(msg.query("MSH.F7"), dolly.query("MSH.F7"));
Ok(())
}
#[test]
fn ensure_to_string() -> Result<(), Hl7ParseError> {
let hl7 = "MSH|^~\\&|GHH LAB|ELAB-3|GHH OE|BLDG4|200202150930||ORU^R01|CNTRL-3456|P|2.4\rOBR|segment";
let msg = Message::try_from(hl7)?;
assert_eq!(msg.to_string(), String::from(hl7));
Ok(())
}
#[test]
fn ensure_message_creation() -> Result<(), Hl7ParseError> {
let hl7 = "MSH|^~\\&|GHH LAB|ELAB-3|GHH OE|BLDG4|200202150930||ORU^R01|CNTRL-3456|P|2.4\rOBR|segment";
let msg0 = Message::try_from(hl7)?;
let msg1 = Message::new(hl7);
assert_eq!(msg0, msg1);
Ok(())
}
#[test]
fn ensure_query() -> Result<(), Hl7ParseError> {
let hl7 = "MSH|^~\\&|GHH LAB|ELAB-3|GHH OE|BLDG4|200202150930||ORU^R01|CNTRL-3456|P|2.4\rOBR|segment^sub&segment";
let msg = Message::try_from(hl7)?;
assert_eq!(msg.query("OBR.F1.R1.C2"), "sub&segment");
assert_eq!(msg.query(&*"OBR.F1.R1.C1".to_string()), "segment"); assert_eq!(msg.query(&*String::from("OBR.F1.R1.C1")), "segment");
assert_eq!(msg.query("MSH.F1"), "^~\\&");
Ok(())
}
#[cfg(feature = "string_index")]
mod string_index_tests {
use super::*;
#[test]
fn ensure_index() -> Result<(), Hl7ParseError> {
let hl7 = "MSH|^~\\&|GHH LAB|ELAB-3|GHH OE|BLDG4|200202150930||ORU^R01|CNTRL-3456|P|2.4\rOBR|segment^sub&segment";
let msg = Message::try_from(hl7)?;
assert_eq!(msg["OBR.F1.R1.C2"], "sub&segment");
assert_eq!(msg[&*"OBR.F1.R1.C1".to_string()], "segment"); assert_eq!(msg[String::from("OBR.F1.R1.C1")], "segment");
assert_eq!(msg[String::from("OBR.F1.C1")], "segment"); assert_eq!(msg[String::from("OBR.F1.R1.C2.S1")], "sub");
println!("{}", Message::parse_query_string("MSH.F2").join("."));
assert_eq!(msg["MSH.F2"], "^~\\&");
Ok(())
}
}
}