#![expect(
clippy::arithmetic_side_effects,
clippy::indexing_slicing,
clippy::manual_let_else,
clippy::string_slice,
reason = "pre-existing query implementation debt moved from staged microcrate into hl7v2; cleanup is split from topology collapse"
)]
pub mod path;
use crate::model::{Atom, Message, Presence, Segment};
pub fn get<'a>(msg: &'a Message, path: &str) -> Option<&'a str> {
let mut parts = path.split('.');
let segment_id = parts.next()?;
let segment = msg
.segments
.iter()
.find(|s| std::str::from_utf8(&s.id) == Ok(segment_id))?;
let field_part = parts.next()?;
let (field_index, rep_index) = parse_field_and_rep(field_part)?;
if segment_id == "MSH" {
get_msh_field(msg, segment, field_index, rep_index, parts)
} else {
get_field(segment, field_index, rep_index, parts)
}
}
pub fn get_presence(msg: &Message, path: &str) -> Presence {
let mut parts = path.split('.');
let segment_id = match parts.next() {
Some(id) => id,
None => return Presence::Missing,
};
let segment = match msg
.segments
.iter()
.find(|s| std::str::from_utf8(&s.id) == Ok(segment_id))
{
Some(seg) => seg,
None => return Presence::Missing,
};
let field_part = match parts.next() {
Some(part) => part,
None => return Presence::Missing,
};
let (field_index, rep_index) = match parse_field_and_rep(field_part) {
Some(indices) => indices,
None => return Presence::Missing,
};
if segment_id == "MSH" {
get_msh_field_presence(msg, segment, field_index, rep_index, parts)
} else {
get_field_presence(segment, field_index, rep_index, parts)
}
}
fn parse_field_and_rep(field_str: &str) -> Option<(usize, usize)> {
if let Some(bracket_pos) = field_str.find('[') {
let field_index = field_str[..bracket_pos].parse::<usize>().ok()?;
let rep_part = &field_str[bracket_pos + 1..];
if let Some(end_bracket) = rep_part.find(']') {
let rep_index = rep_part[..end_bracket].parse::<usize>().ok()?;
Some((field_index, rep_index))
} else {
None
}
} else {
let field_index = field_str.parse::<usize>().ok()?;
Some((field_index, 1))
}
}
fn get_field<'a>(
segment: &'a Segment,
field_index: usize,
rep_index: usize,
mut parts: std::str::Split<char>,
) -> Option<&'a str> {
if field_index == 0 {
return None;
}
let zero_based_field_index = field_index - 1;
if zero_based_field_index >= segment.fields.len() {
return None;
}
let field = &segment.fields[zero_based_field_index];
if rep_index == 0 || rep_index > field.reps.len() {
return None;
}
let rep = &field.reps[rep_index - 1];
let comp_index = if let Some(comp_part) = parts.next() {
comp_part.parse::<usize>().ok()?
} else {
1 };
if comp_index == 0 || comp_index > rep.comps.len() {
return None;
}
let comp = &rep.comps[comp_index - 1];
if comp.subs.is_empty() {
return None;
}
match &comp.subs[0] {
Atom::Text(text) => Some(text.as_str()),
Atom::Null => None,
}
}
fn get_msh_field<'a>(
_msg: &'a Message,
segment: &'a Segment,
field_index: usize,
rep_index: usize,
mut parts: std::str::Split<char>,
) -> Option<&'a str> {
if field_index == 1 {
None } else if field_index == 2 {
if segment.fields.is_empty() {
return None;
}
let field = &segment.fields[0];
if rep_index == 0 || rep_index > field.reps.len() {
return None;
}
let rep = &field.reps[rep_index - 1];
let comp_index = if let Some(comp_part) = parts.next() {
comp_part.parse::<usize>().ok()?
} else {
1
};
if comp_index == 0 || comp_index > rep.comps.len() {
return None;
}
let comp = &rep.comps[comp_index - 1];
if comp.subs.is_empty() {
return None;
}
match &comp.subs[0] {
Atom::Text(text) => Some(text.as_str()),
Atom::Null => None,
}
} else {
let adjusted_field_index = field_index - 2;
if adjusted_field_index >= segment.fields.len() {
return None;
}
let field = &segment.fields[adjusted_field_index];
if rep_index == 0 || rep_index > field.reps.len() {
return None;
}
let rep = &field.reps[rep_index - 1];
let comp_index = if let Some(comp_part) = parts.next() {
comp_part.parse::<usize>().ok()?
} else {
1
};
if comp_index == 0 || comp_index > rep.comps.len() {
return None;
}
let comp = &rep.comps[comp_index - 1];
if comp.subs.is_empty() {
return None;
}
match &comp.subs[0] {
Atom::Text(text) => Some(text.as_str()),
Atom::Null => None,
}
}
}
fn get_field_presence(
segment: &Segment,
field_index: usize,
rep_index: usize,
mut parts: std::str::Split<char>,
) -> Presence {
if field_index == 0 {
return Presence::Missing;
}
let zero_based_field_index = field_index - 1;
if zero_based_field_index >= segment.fields.len() {
return Presence::Missing;
}
let field = &segment.fields[zero_based_field_index];
if rep_index == 0 || rep_index > field.reps.len() {
return Presence::Missing;
}
let rep = &field.reps[rep_index - 1];
let comp_index = if let Some(comp_part) = parts.next() {
match comp_part.parse::<usize>() {
Ok(index) => index,
Err(_) => return Presence::Missing,
}
} else {
1
};
if comp_index == 0 || comp_index > rep.comps.len() {
return Presence::Missing;
}
let comp = &rep.comps[comp_index - 1];
if comp.subs.is_empty() {
return Presence::Missing;
}
match &comp.subs[0] {
Atom::Text(text) => {
if text.is_empty() {
Presence::Empty
} else {
Presence::Value(text.clone())
}
}
Atom::Null => Presence::Null,
}
}
fn get_msh_field_presence(
msg: &Message,
segment: &Segment,
field_index: usize,
rep_index: usize,
mut parts: std::str::Split<char>,
) -> Presence {
if field_index == 1 {
Presence::Value(msg.delims.field.to_string())
} else if field_index == 2 {
if segment.fields.is_empty() {
return Presence::Missing;
}
let field = &segment.fields[0];
if rep_index == 0 || rep_index > field.reps.len() {
return Presence::Missing;
}
let rep = &field.reps[rep_index - 1];
let comp_index = if let Some(comp_part) = parts.next() {
match comp_part.parse::<usize>() {
Ok(index) => index,
Err(_) => return Presence::Missing,
}
} else {
1
};
if comp_index == 0 || comp_index > rep.comps.len() {
return Presence::Missing;
}
let comp = &rep.comps[comp_index - 1];
if comp.subs.is_empty() {
return Presence::Missing;
}
match &comp.subs[0] {
Atom::Text(text) => {
if text.is_empty() {
Presence::Empty
} else {
Presence::Value(text.clone())
}
}
Atom::Null => Presence::Null,
}
} else {
let adjusted_field_index = field_index - 2;
if adjusted_field_index >= segment.fields.len() {
return Presence::Missing;
}
let field = &segment.fields[adjusted_field_index];
if rep_index == 0 || rep_index > field.reps.len() {
return Presence::Missing;
}
let rep = &field.reps[rep_index - 1];
let comp_index = if let Some(comp_part) = parts.next() {
match comp_part.parse::<usize>() {
Ok(index) => index,
Err(_) => return Presence::Missing,
}
} else {
1
};
if comp_index == 0 || comp_index > rep.comps.len() {
return Presence::Missing;
}
let comp = &rep.comps[comp_index - 1];
if comp.subs.is_empty() {
return Presence::Missing;
}
match &comp.subs[0] {
Atom::Text(text) => {
if text.is_empty() {
Presence::Empty
} else {
Presence::Value(text.clone())
}
}
Atom::Null => Presence::Null,
}
}
}
#[cfg(test)]
mod tests;