mod parser;
use std::{fmt::Display, str::FromStr};
pub use parser::QueryParseError;
use thiserror::Error;
use crate::{
message::{Component, Field, Repeat, Segment, Separators, Subcomponent},
parser::Span,
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LocationQuery {
pub segment: String,
pub segment_index: Option<usize>,
pub field: Option<usize>,
pub repeat: Option<usize>,
pub component: Option<usize>,
pub subcomponent: Option<usize>,
}
pub fn parse_location_query(query: &str) -> Result<LocationQuery, QueryParseError> {
parser::parse_query(Span::new(query))
.map(|(_, m)| m)
.map_err(|e| e.into())
}
impl FromStr for LocationQuery {
type Err = QueryParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
parse_location_query(s)
}
}
impl TryFrom<&str> for LocationQuery {
type Error = QueryParseError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
parse_location_query(value)
}
}
impl TryFrom<String> for LocationQuery {
type Error = QueryParseError;
fn try_from(value: String) -> Result<Self, Self::Error> {
parse_location_query(&value)
}
}
impl TryFrom<&String> for LocationQuery {
type Error = QueryParseError;
fn try_from(value: &String) -> Result<Self, Self::Error> {
parse_location_query(value)
}
}
impl Display for LocationQuery {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.segment)?;
if let Some(i) = self.segment_index {
write!(f, "[{}]", i)?;
}
if let Some(i) = self.field {
write!(f, ".{}", i)?;
} else {
return Ok(());
}
if let Some(i) = self.repeat {
write!(f, "[{}]", i)?;
}
if let Some(i) = self.component {
write!(f, ".{}", i)?;
} else {
return Ok(());
}
if let Some(i) = self.subcomponent {
write!(f, ".{}", i)?;
}
Ok(())
}
}
impl LocationQuery {
pub fn parse(query: &str) -> Result<Self, QueryParseError> {
parse_location_query(query)
}
}
#[derive(Debug, Clone)]
pub struct LocationQueryBuilder {
segment: Option<String>,
segment_index: Option<usize>,
field: Option<usize>,
repeat: Option<usize>,
component: Option<usize>,
subcomponent: Option<usize>,
}
#[derive(Debug, Clone, Error)]
pub enum LocationQueryBuildError {
#[error("Missing segment: segment is required")]
MissingSegment,
#[error("Invalid segment length: segments must be 3 characters long")]
InvalidSegmentLength,
#[error("Invalid segment name: segments must be ASCII uppercase")]
InvalidSegmentName,
#[error("Invalid segment index: segment index must be greater than 0")]
InvalidSegmentIndex,
#[error("Invalid field index: field index must be greater than 0")]
InvalidFieldIndex,
#[error("Invalid repeat index: repeat index must be greater than 0")]
InvalidRepeatIndex,
#[error("Invalid component index: component index must be greater than 0")]
InvalidComponentIndex,
#[error("Invalid subcomponent index: subcomponent index must be greater than 0")]
InvalidSubcomponentIndex,
}
impl Default for LocationQueryBuilder {
fn default() -> Self {
Self::new()
}
}
impl LocationQueryBuilder {
pub fn new() -> Self {
Self {
segment: None,
segment_index: None,
field: None,
repeat: None,
component: None,
subcomponent: None,
}
}
pub fn segment<S: ToString>(mut self, segment: S) -> Self {
self.segment = Some(segment.to_string());
self
}
pub fn segment_index(mut self, index: usize) -> Self {
self.segment_index = Some(index);
self
}
pub fn field(mut self, index: usize) -> Self {
self.field = Some(index);
self
}
pub fn repeat(mut self, index: usize) -> Self {
self.repeat = Some(index);
self
}
pub fn component(mut self, index: usize) -> Self {
self.component = Some(index);
self
}
pub fn subcomponent(mut self, index: usize) -> Self {
self.subcomponent = Some(index);
self
}
pub fn build(self) -> Result<LocationQuery, LocationQueryBuildError> {
let segment = if let Some(segment) = self.segment {
if segment.len() != 3 {
return Err(LocationQueryBuildError::InvalidSegmentLength);
}
if !segment.chars().all(|c| c.is_ascii_uppercase()) {
return Err(LocationQueryBuildError::InvalidSegmentName);
}
segment
} else {
return Err(LocationQueryBuildError::MissingSegment);
};
let segment_index = if let Some(segment_index) = self.segment_index {
if segment_index == 0 {
return Err(LocationQueryBuildError::InvalidSegmentIndex);
}
Some(segment_index)
} else {
None
};
let field = if let Some(field) = self.field {
if field == 0 {
return Err(LocationQueryBuildError::InvalidFieldIndex);
}
Some(field)
} else {
None
};
let repeat = if let Some(repeat) = self.repeat {
if repeat == 0 {
return Err(LocationQueryBuildError::InvalidRepeatIndex);
}
Some(repeat)
} else {
None
};
let component = if let Some(component) = self.component {
if component == 0 {
return Err(LocationQueryBuildError::InvalidComponentIndex);
}
Some(component)
} else {
None
};
let subcomponent = if let Some(subcomponent) = self.subcomponent {
if subcomponent == 0 {
return Err(LocationQueryBuildError::InvalidSubcomponentIndex);
}
Some(subcomponent)
} else {
None
};
Ok(LocationQuery {
segment,
segment_index,
field,
repeat,
component,
subcomponent,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub enum LocationQueryResult<'m> {
Segment(&'m Segment<'m>),
Field(&'m Field<'m>),
Repeat(&'m Repeat<'m>),
Component(&'m Component<'m>),
Subcomponent(&'m Subcomponent<'m>),
}
impl<'m> LocationQueryResult<'m> {
pub fn raw_value(&self) -> &'m str {
match self {
LocationQueryResult::Segment(seg) => seg.raw_value(),
LocationQueryResult::Field(field) => field.raw_value(),
LocationQueryResult::Repeat(repeat) => repeat.raw_value(),
LocationQueryResult::Component(component) => component.raw_value(),
LocationQueryResult::Subcomponent(subcomponent) => subcomponent.raw_value(),
}
}
pub fn range(&self) -> std::ops::Range<usize> {
match self {
LocationQueryResult::Segment(seg) => seg.range.clone(),
LocationQueryResult::Field(field) => field.range.clone(),
LocationQueryResult::Repeat(repeat) => repeat.range.clone(),
LocationQueryResult::Component(component) => component.range.clone(),
LocationQueryResult::Subcomponent(subcomponent) => subcomponent.range.clone(),
}
}
pub fn display(&self, separators: &'m Separators) -> LocationQueryResultDisplay<'m> {
LocationQueryResultDisplay {
value: self.raw_value(),
separators,
}
}
}
pub struct LocationQueryResultDisplay<'m> {
value: &'m str,
separators: &'m Separators,
}
impl<'m> Display for LocationQueryResultDisplay<'m> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() {
write!(f, "{}", self.value)
} else {
write!(f, "{}", self.separators.decode(self.value))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions_sorted::assert_eq;
#[test]
fn can_display_location_query() {
let query = LocationQuery {
segment: "MSH".to_string(),
segment_index: Some(1),
field: Some(2),
repeat: Some(3),
component: Some(4),
subcomponent: Some(5),
};
assert_eq!(query.to_string(), "MSH[1].2[3].4.5");
let query = LocationQuery {
segment: "MSH".to_string(),
segment_index: None,
field: Some(2),
repeat: None,
component: Some(4),
subcomponent: None,
};
assert_eq!(query.to_string(), "MSH.2.4");
let query = LocationQuery {
segment: "MSH".to_string(),
segment_index: None,
field: None,
repeat: None,
component: Some(4),
subcomponent: Some(5),
};
assert_eq!(query.to_string(), "MSH");
}
}