use serde::{Deserialize, Serialize};
use super::common::{Cardinality, CodeDefinition};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MigSchema {
pub message_type: String,
pub variant: Option<String>,
pub version: String,
pub publication_date: String,
pub author: String,
pub format_version: String,
pub source_file: String,
pub segments: Vec<MigSegment>,
pub segment_groups: Vec<MigSegmentGroup>,
}
impl MigSchema {
pub fn includes_envelope(&self) -> bool {
self.segments.iter().any(|s| s.id == "UNA" || s.id == "UNB")
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MigSegment {
pub id: String,
pub name: String,
pub description: Option<String>,
pub counter: Option<String>,
pub level: i32,
pub number: Option<String>,
pub max_rep_std: i32,
pub max_rep_spec: i32,
pub status_std: Option<String>,
pub status_spec: Option<String>,
pub example: Option<String>,
pub data_elements: Vec<MigDataElement>,
pub composites: Vec<MigComposite>,
}
impl MigSegment {
pub fn cardinality(&self) -> Cardinality {
let status = self
.status_spec
.as_deref()
.or(self.status_std.as_deref())
.unwrap_or("C");
Cardinality::from_status(status)
}
pub fn max_rep(&self) -> i32 {
self.max_rep_spec.max(self.max_rep_std)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MigSegmentGroup {
pub id: String,
pub name: String,
pub description: Option<String>,
pub counter: Option<String>,
pub level: i32,
pub max_rep_std: i32,
pub max_rep_spec: i32,
pub status_std: Option<String>,
pub status_spec: Option<String>,
pub segments: Vec<MigSegment>,
pub nested_groups: Vec<MigSegmentGroup>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub variant_code: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub variant_qualifier_position: Option<(usize, usize)>,
#[serde(default)]
pub variant_codes: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub merged_variant_count: Option<u32>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub variant_entry_qualifiers: Vec<EntryQualifier>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct EntryQualifier {
pub tag: String,
pub element: usize,
pub component: usize,
pub codes: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub nested_group_ids: Vec<String>,
}
impl EntryQualifier {
pub fn same_qualifier(&self, other: &EntryQualifier) -> bool {
self.tag == other.tag
&& self.element == other.element
&& self.component == other.component
&& self.codes == other.codes
}
pub fn matches(&self, elements: &[Vec<String>]) -> bool {
elements
.get(self.element)
.and_then(|e| e.get(self.component))
.is_some_and(|v| self.codes.iter().any(|c| c.eq_ignore_ascii_case(v)))
}
}
impl MigSegmentGroup {
pub fn entry_qualifier(&self) -> Option<EntryQualifier> {
let entry = self.segments.first()?;
let simple = entry.data_elements.iter().map(|d| (d.position, 0, d));
let composite = entry.composites.iter().flat_map(|c| {
c.data_elements
.iter()
.map(move |d| (c.position, d.position, d))
});
simple
.chain(composite)
.filter(|(_, _, d)| d.codes.iter().any(|c| !c.value.is_empty()))
.min_by_key(|(e, c, _)| (*e, *c))
.map(|(element, component, d)| EntryQualifier {
tag: entry.id.clone(),
element,
component,
codes: d
.codes
.iter()
.filter(|c| !c.value.is_empty())
.map(|c| c.value.clone())
.collect(),
nested_group_ids: self.nested_group_ids(),
})
}
pub fn nested_group_ids(&self) -> Vec<String> {
let mut ids: Vec<String> = Vec::new();
for g in &self.nested_groups {
if !ids.contains(&g.id) {
ids.push(g.id.clone());
}
}
ids
}
pub fn cardinality(&self) -> Cardinality {
let status = self
.status_spec
.as_deref()
.or(self.status_std.as_deref())
.unwrap_or("C");
Cardinality::from_status(status)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MigComposite {
pub id: String,
pub name: String,
pub description: Option<String>,
pub status_std: Option<String>,
pub status_spec: Option<String>,
pub data_elements: Vec<MigDataElement>,
pub position: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MigDataElement {
pub id: String,
pub name: String,
pub description: Option<String>,
pub status_std: Option<String>,
pub status_spec: Option<String>,
pub format_std: Option<String>,
pub format_spec: Option<String>,
pub codes: Vec<CodeDefinition>,
pub position: usize,
}