use crate::model::{Atom, Comp, Error, Field, Message, Rep, Segment};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AckCode {
AA,
AE,
AR,
CA,
CE,
CR,
}
impl AckCode {
pub fn as_str(&self) -> &'static str {
match self {
AckCode::AA => "AA",
AckCode::AE => "AE",
AckCode::AR => "AR",
AckCode::CA => "CA",
AckCode::CE => "CE",
AckCode::CR => "CR",
}
}
}
impl std::fmt::Display for AckCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
pub fn ack(original: &Message, code: AckCode) -> Result<Message, Error> {
let delims = original.delims.clone();
let msh_segment = create_ack_msh_segment(original)?;
let msa_segment = create_msa_segment(original, code)?;
Ok(Message {
delims,
segments: vec![msh_segment, msa_segment],
charsets: vec![],
})
}
fn create_ack_msh_segment(original: &Message) -> Result<Segment, Error> {
let original_msh = original.segments.first().ok_or(Error::InvalidSegmentId)?;
if &original_msh.id != b"MSH" {
return Err(Error::InvalidSegmentId);
}
let sending_app = get_field_value(original_msh, 2).unwrap_or_else(|| "HL7V2RS".to_string());
let sending_fac = get_field_value(original_msh, 3).unwrap_or_else(|| "HL7V2RS".to_string());
let receiving_app = get_field_value(original_msh, 4).unwrap_or_default();
let receiving_fac = get_field_value(original_msh, 5).unwrap_or_default();
let message_type = get_field_value(original_msh, 8).unwrap_or_else(|| "ACK".to_string());
let control_id = get_field_value(original_msh, 9).unwrap_or_default();
let processing_id = get_field_value(original_msh, 10).unwrap_or_else(|| "P".to_string());
let version = get_field_value(original_msh, 11).unwrap_or_else(|| "2.5.1".to_string());
let timestamp = chrono::Utc::now().format("%Y%m%d%H%M%S").to_string();
let mut fields = Vec::new();
fields.push(Field {
reps: vec![Rep {
comps: vec![Comp {
subs: vec![Atom::Text(format!(
"{}{}{}{}",
original.delims.comp,
original.delims.rep,
original.delims.esc,
original.delims.sub
))],
}],
}],
});
fields.push(Field {
reps: vec![Rep {
comps: vec![Comp {
subs: vec![Atom::Text(receiving_app)],
}],
}],
});
fields.push(Field {
reps: vec![Rep {
comps: vec![Comp {
subs: vec![Atom::Text(receiving_fac)],
}],
}],
});
fields.push(Field {
reps: vec![Rep {
comps: vec![Comp {
subs: vec![Atom::Text(sending_app)],
}],
}],
});
fields.push(Field {
reps: vec![Rep {
comps: vec![Comp {
subs: vec![Atom::Text(sending_fac)],
}],
}],
});
fields.push(Field {
reps: vec![Rep {
comps: vec![Comp {
subs: vec![Atom::Text(timestamp)],
}],
}],
});
fields.push(Field {
reps: vec![Rep {
comps: vec![Comp {
subs: vec![Atom::Text(String::new())],
}],
}],
});
fields.push(Field {
reps: vec![Rep {
comps: vec![
Comp {
subs: vec![Atom::Text("ACK".to_string())],
},
Comp {
subs: vec![Atom::Text(message_type.clone())],
},
],
}],
});
fields.push(Field {
reps: vec![Rep {
comps: vec![Comp {
subs: vec![Atom::Text(control_id)],
}],
}],
});
fields.push(Field {
reps: vec![Rep {
comps: vec![Comp {
subs: vec![Atom::Text(processing_id)],
}],
}],
});
fields.push(Field {
reps: vec![Rep {
comps: vec![Comp {
subs: vec![Atom::Text(version)],
}],
}],
});
Ok(Segment {
id: *b"MSH",
fields,
})
}
fn create_msa_segment(original: &Message, code: AckCode) -> Result<Segment, Error> {
let original_msh = original.segments.first().ok_or(Error::InvalidSegmentId)?;
if &original_msh.id != b"MSH" {
return Err(Error::InvalidSegmentId);
}
let control_id = get_field_value(original_msh, 9).unwrap_or_default();
let fields = vec![
Field {
reps: vec![Rep {
comps: vec![Comp {
subs: vec![Atom::Text(code.as_str().to_string())],
}],
}],
},
Field {
reps: vec![Rep {
comps: vec![Comp {
subs: vec![Atom::Text(control_id)],
}],
}],
},
];
Ok(Segment {
id: *b"MSA",
fields,
})
}
fn get_field_value(segment: &Segment, field_index: usize) -> Option<String> {
let field = segment.fields.get(field_index.checked_sub(1)?)?;
let rep = field.reps.first()?;
let comp = rep.comps.first()?;
match comp.subs.first()? {
Atom::Text(text) => Some(text.clone()),
Atom::Null => None,
}
}
pub fn ack_with_error(
original: &Message,
code: AckCode,
error_message: Option<&str>,
) -> Result<Message, Error> {
let mut ack_msg = ack(original, code)?;
if let Some(msg) = error_message {
let err_segment = create_err_segment(msg);
ack_msg.segments.push(err_segment);
}
Ok(ack_msg)
}
fn create_err_segment(error_message: &str) -> Segment {
let fields = vec![
Field {
reps: vec![Rep {
comps: vec![Comp {
subs: vec![Atom::Text(String::new())],
}],
}],
},
Field {
reps: vec![Rep {
comps: vec![Comp {
subs: vec![Atom::Text(String::new())],
}],
}],
},
Field {
reps: vec![Rep {
comps: vec![Comp {
subs: vec![Atom::Text(error_message.to_string())],
}],
}],
},
];
Segment {
id: *b"ERR",
fields,
}
}