1use std::{collections::HashMap, fmt::Display, ops::Range};
2
3use crate::{
4 message::{
5 Component, DecodedSeparatorsDisplay, Field, Repeat, Segment, Separators, Subcomponent,
6 },
7 Message,
8};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize))]
13pub struct LocatedCursor<'s> {
14 pub message: &'s Message<'s>,
15 pub segment: Option<(&'s str, usize, &'s Segment<'s>)>,
17 pub field: Option<(usize, &'s Field<'s>)>,
19 pub repeat: Option<(usize, &'s Repeat<'s>)>,
21 pub component: Option<(usize, &'s Component<'s>)>,
23 pub sub_component: Option<(usize, &'s Subcomponent<'s>)>,
25}
26
27pub fn locate_cursor<'m>(message: &'m Message<'m>, offset: usize) -> Option<LocatedCursor<'m>> {
30 let mut cursor = LocatedCursor {
31 message,
32 segment: None,
33 field: None,
34 repeat: None,
35 component: None,
36 sub_component: None,
37 };
38
39 if offset >= message.source.len() {
40 return None;
41 }
42 let mut seg_indices: HashMap<&str, usize> = HashMap::new();
43 for seg in message.segments() {
44 *seg_indices.entry(seg.name).or_insert(0) += 1;
45 if offset >= seg.range.start && offset <= seg.range.end {
47 cursor.segment = Some((
48 seg.name,
49 *seg_indices.get(seg.name).expect("seg exists in hashmap"),
50 seg,
51 ));
52 break;
53 }
54 }
55
56 cursor.segment?;
57 for (i, field) in cursor.segment.as_ref().unwrap().2.fields().enumerate() {
58 if offset >= field.range.start && offset <= field.range.end {
60 cursor.field = Some((i + 1, field));
61 break;
62 }
63 }
64
65 if cursor.field.is_none() {
66 return Some(cursor);
67 }
68 for (i, repeat) in cursor.field.as_ref().unwrap().1.repeats().enumerate() {
69 if offset >= repeat.range.start && offset <= repeat.range.end {
71 cursor.repeat = Some((i + 1, repeat));
72 break;
73 }
74 }
75
76 if cursor.repeat.is_none() {
77 return Some(cursor);
78 }
79 for (i, component) in cursor.repeat.as_ref().unwrap().1.components().enumerate() {
80 if offset >= component.range.start && offset <= component.range.end {
82 cursor.component = Some((i + 1, component));
83 break;
84 }
85 }
86
87 if cursor.component.is_none() {
88 return Some(cursor);
89 }
90 for (i, sub_component) in cursor
91 .component
92 .as_ref()
93 .unwrap()
94 .1
95 .subcomponents()
96 .enumerate()
97 {
98 if offset >= sub_component.range.start && offset <= sub_component.range.end {
100 cursor.sub_component = Some((i + 1, sub_component));
101 break;
102 }
103 }
104
105 Some(cursor)
106}
107
108impl Display for LocatedCursor<'_> {
109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 if let Some((seg_name, seg_idx, _)) = self.segment {
111 write!(f, "{}", seg_name)?;
112 if self.message.segment_count(seg_name) > 1 {
113 write!(f, "[{}]", seg_idx)?;
114 }
115 }
116 if let Some((field_idx, field)) = self.field {
117 write!(f, ".{}", field_idx)?;
118 if let Some((repeat_idx, repeat)) = self.repeat {
119 if field.has_repeats() {
120 write!(f, "[{}]", repeat_idx)?;
121 }
122 if let Some((component_idx, component)) = self.component {
123 if repeat.has_components() {
124 write!(f, ".{}", component_idx)?;
125 }
126 if let Some((sub_component_idx, _)) = self.sub_component {
127 if component.has_subcomponents() {
128 write!(f, ".{}", sub_component_idx)?;
129 }
130 }
131 }
132 }
133 }
134 Ok(())
135 }
136}
137
138impl<'m> LocatedCursor<'m> {
139 pub fn raw_value(&self) -> Option<&str> {
143 self.sub_component
144 .map(|(_, sub_component)| sub_component.raw_value())
145 .or_else(|| self.component.map(|(_, component)| component.raw_value()))
146 .or_else(|| self.repeat.map(|(_, repeat)| repeat.raw_value()))
147 .or_else(|| self.field.map(|(_, field)| field.raw_value()))
148 }
149
150 pub fn range(&self) -> Option<&'m Range<usize>> {
154 self.sub_component
155 .map(|(_, sub_component)| &sub_component.range)
156 .or_else(|| self.component.map(|(_, component)| &component.range))
157 .or_else(|| self.repeat.map(|(_, repeat)| &repeat.range))
158 .or_else(|| self.field.map(|(_, field)| &field.range))
159 }
160
161 pub fn value(&'m self, separators: &'m Separators) -> Option<DecodedSeparatorsDisplay<'m>> {
165 self.raw_value()
166 .map(|raw_value| separators.decode(raw_value))
167 }
168}
169
170#[cfg(test)]
171mod test {
172 use super::*;
173
174 #[test]
175 fn can_locate_cursor_in_segment() {
176 let message = Message::parse("MSH|^~\\&|asdf\rPID|1|0").unwrap();
177 let cursor = locate_cursor(&message, 0).expect("cursor is located");
178 assert_eq!(cursor.segment.unwrap().0, "MSH");
179 assert_eq!(cursor.segment.unwrap().1, 1);
180
181 let cursor = locate_cursor(&message, 18).expect("cursor is located");
182 assert_eq!(cursor.segment.unwrap().0, "PID");
183 assert_eq!(cursor.segment.unwrap().1, 1);
184 assert_eq!(cursor.field.unwrap().0, 1);
185 assert_eq!(cursor.field.unwrap().1.raw_value(), "1");
186 }
187
188 #[test]
189 fn can_locate_cursor_in_repeated_segment() {
190 let message = Message::parse("MSH|^~\\&|asdf\rPID|1|0\rPID|2|1").unwrap();
191 let cursor = locate_cursor(&message, 26).expect("cursor is located");
192 assert_eq!(cursor.segment.unwrap().0, "PID");
193 assert_eq!(cursor.segment.unwrap().1, 2);
194 assert_eq!(cursor.field.unwrap().0, 1);
195 assert_eq!(cursor.field.unwrap().1.raw_value(), "2");
196 }
197
198 #[test]
199 fn can_locate_cursor_at_field_boundaries() {
200 let message = Message::parse("MSH|^~\\&|asdf\rPID|1|0").unwrap();
201 let cursor = locate_cursor(&message, 19).expect("cursor is located");
202 assert_eq!(cursor.segment.unwrap().0, "PID");
203 assert_eq!(cursor.segment.unwrap().1, 1);
204 assert_eq!(cursor.field.unwrap().0, 1);
205 assert_eq!(cursor.field.unwrap().1.raw_value(), "1");
206
207 let message = Message::parse("MSH|^~\\&|asdf\rPID||0").unwrap();
208 let cursor = locate_cursor(&message, 18).expect("cursor is located");
209 assert_eq!(cursor.segment.unwrap().0, "PID");
210 assert_eq!(cursor.segment.unwrap().1, 1);
211 assert_eq!(cursor.field.unwrap().0, 1);
212 assert_eq!(cursor.field.unwrap().1.raw_value(), "");
213 }
214
215 #[test]
216 fn can_get_cursor_location_value() {
217 let message = Message::parse("MSH|^~\\&|asdf\rPID|1\\S\\2|0").unwrap();
218 let cursor = locate_cursor(&message, 19).expect("cursor is located");
219 let value = cursor
220 .value(&message.separators)
221 .expect("value is decoded")
222 .to_string();
223 assert_eq!(value, "1^2");
224 }
225}