hl7-parser 0.3.0

Parses the structure of HL7v2 messages, but does not validate the correctness of the messages.
Documentation
use super::ComponentBuilder;
use crate::{
    datetime::TimeStamp,
    message::{Repeat, Separators},
};
use display::RepeatBuilderDisplay;
use std::{collections::HashMap, fmt::Display};

#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum RepeatBuilder {
    Value(String),
    Components(HashMap<usize, ComponentBuilder>),
}

impl Default for RepeatBuilder {
    fn default() -> Self {
        RepeatBuilder::Value(String::new())
    }
}

impl RepeatBuilder {
    pub fn with_value(value: String) -> Self {
        RepeatBuilder::Value(value)
    }

    pub fn with_components(components: HashMap<usize, ComponentBuilder>) -> Self {
        RepeatBuilder::Components(components)
    }

    pub fn value(&self) -> Option<&String> {
        match self {
            RepeatBuilder::Value(value) => Some(value),
            _ => None,
        }
    }

    pub fn components(&self) -> Option<&HashMap<usize, ComponentBuilder>> {
        match self {
            RepeatBuilder::Components(components) => Some(components),
            _ => None,
        }
    }

    pub fn value_mut(&mut self) -> Option<&mut String> {
        match self {
            RepeatBuilder::Value(value) => Some(value),
            _ => None,
        }
    }

    pub fn components_mut(&mut self) -> Option<&mut HashMap<usize, ComponentBuilder>> {
        match self {
            RepeatBuilder::Components(components) => Some(components),
            _ => None,
        }
    }

    pub fn has_components(&self) -> bool {
        matches!(self, RepeatBuilder::Components(_))
    }

    pub fn into_value(self) -> Option<String> {
        match self {
            RepeatBuilder::Value(value) => Some(value),
            _ => None,
        }
    }

    pub fn into_components(self) -> Option<HashMap<usize, ComponentBuilder>> {
        match self {
            RepeatBuilder::Components(components) => Some(components),
            _ => None,
        }
    }

    pub fn is_empty(&self) -> bool {
        match self {
            RepeatBuilder::Value(value) => value.is_empty(),
            RepeatBuilder::Components(components) => components.is_empty(),
        }
    }

    pub fn set_value<S: ToString>(&mut self, value: S) {
        *self = RepeatBuilder::Value(value.to_string());
    }

    pub fn set_timestamp<T: Into<TimeStamp>>(&mut self, timestamp: T) {
        *self = RepeatBuilder::Value(timestamp.into().to_string());
    }

    pub fn set_components(&mut self, components: HashMap<usize, ComponentBuilder>) {
        *self = RepeatBuilder::Components(components);
    }

    pub fn set_component<C: Into<ComponentBuilder>>(&mut self, index: usize, component: C) {
        debug_assert!(index > 0, "Component numbers are 1-based");
        match self {
            RepeatBuilder::Components(components) => {
                components.insert(index, component.into());
            }
            _ => {
                let mut components = HashMap::new();
                components.insert(index, component.into());
                *self = RepeatBuilder::Components(components);
            }
        }
    }

    pub fn set_component_value<S: ToString>(&mut self, index: usize, value: S) {
        debug_assert!(index > 0, "Component numbers are 1-based");
        match self {
            RepeatBuilder::Components(components) => {
                let component = components
                    .entry(index)
                    .or_insert(ComponentBuilder::default());
                component.set_value(value);
            }
            _ => {
                let mut components = HashMap::new();
                let component = ComponentBuilder::Value(value.to_string());
                components.insert(index, component);
                *self = RepeatBuilder::Components(components);
            }
        }
    }

    pub fn clear(&mut self) {
        *self = RepeatBuilder::Value(String::new());
    }

    pub fn component(&self, index: usize) -> Option<&ComponentBuilder> {
        debug_assert!(index > 0, "Component numbers are 1-based");
        match self {
            RepeatBuilder::Components(components) => components.get(&index),
            _ => None,
        }
    }

    pub fn component_mut(&mut self, index: usize) -> Option<&mut ComponentBuilder> {
        debug_assert!(index > 0, "Component numbers are 1-based");
        match self {
            RepeatBuilder::Components(components) => components.get_mut(&index),
            _ => None,
        }
    }

    pub fn remove_component(&mut self, index: usize) -> Option<ComponentBuilder> {
        debug_assert!(index > 0, "Component numbers are 1-based");
        match self {
            RepeatBuilder::Components(components) => components.remove(&index),
            _ => None,
        }
    }

    pub fn display<'a>(&'a self, separators: &'a Separators) -> RepeatBuilderDisplay<'a> {
        RepeatBuilderDisplay {
            repeat: self,
            separators,
        }
    }

    pub fn from_component_map<I: Into<usize>, C: Into<ComponentBuilder>>(
        components: HashMap<I, C>,
    ) -> Self {
        let components = components
            .into_iter()
            .map(|(i, c)| (i.into(), c.into()))
            .collect();
        RepeatBuilder::Components(components)
    }
}

mod display {
    use super::*;

    pub struct RepeatBuilderDisplay<'a> {
        pub(super) repeat: &'a RepeatBuilder,
        pub(super) separators: &'a Separators,
    }

    impl<'a> Display for RepeatBuilderDisplay<'a> {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            match self.repeat {
                RepeatBuilder::Value(value) => self.separators.encode(value).fmt(f),
                RepeatBuilder::Components(components) => {
                    if components.is_empty() {
                        return Ok(());
                    }
                    let max_index = components.keys().max().unwrap();
                    for i in 1..=*max_index {
                        if let Some(component) = components.get(&i) {
                            write!(f, "{}", component.display(self.separators))?;
                        }
                        if i < *max_index {
                            write!(f, "{}", self.separators.component)?;
                        }
                    }
                    Ok(())
                }
            }
        }
    }
}

impl<S: ToString> From<S> for RepeatBuilder {
    fn from(value: S) -> Self {
        RepeatBuilder::Value(value.to_string())
    }
}

impl<'m> From<&'m Repeat<'m>> for RepeatBuilder {
    fn from(repeat: &'m Repeat<'m>) -> Self {
        if repeat.has_components() {
            let components = repeat
                .components
                .iter()
                .enumerate()
                .map(|(i, c)| (i + 1, c.into()))
                .collect();
            RepeatBuilder::Components(components)
        } else {
            RepeatBuilder::Value(repeat.raw_value().to_string())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use pretty_assertions_sorted::{assert_eq, assert_eq_sorted};

    #[test]
    fn can_display_repeat_builder() {
        let separators = Separators::default();
        let repeat = RepeatBuilder::with_value("value".to_string());
        let display = repeat.display(&separators).to_string();
        assert_eq!(display, "value");

        let mut components = HashMap::new();
        components.insert(1, ComponentBuilder::with_value("foo".to_string()));
        components.insert(3, ComponentBuilder::with_value("bar".to_string()));
        let repeat = RepeatBuilder::with_components(components);
        let display = repeat.display(&separators).to_string();
        assert_eq!(display, "foo^^bar");
    }

    #[test]
    fn can_convert_repeat_to_repeat_builder() {
        let repeat = crate::parser::parse_repeat("foo^^bar").expect("Can parse repeat");
        let repeat_builder = RepeatBuilder::from(&repeat);
        assert_eq_sorted!(
            repeat_builder,
            RepeatBuilder::with_components({
                let mut components = HashMap::new();
                components.insert(1, ComponentBuilder::with_value("foo".to_string()));
                components.insert(2, ComponentBuilder::with_value("".to_string()));
                components.insert(3, ComponentBuilder::with_value("bar".to_string()));
                components
            })
        );
    }
}