openscenario_rs/parser/
choice_groups.rs1use crate::error::{Error, Result};
58
59pub trait XsdChoiceGroup: Sized {
65 type Variant;
67
68 fn choice_element_names() -> &'static [&'static str];
70
71 fn parse_choice_element(element_name: &str, xml: &str) -> Result<Self::Variant>;
73
74 fn from_choice_variants(variants: Vec<Self::Variant>) -> Result<Self>;
76}
77
78pub struct ChoiceGroupParser {
84 xml: String,
85}
86
87impl ChoiceGroupParser {
88 pub fn from_str(xml: &str) -> Self {
90 Self {
91 xml: xml.to_string(),
92 }
93 }
94
95 pub fn parse_choice_group<T: XsdChoiceGroup>(&self, container_element: &str) -> Result<T> {
97 let choice_names = T::choice_element_names();
98 let mut variants: Vec<(usize, T::Variant)> = Vec::new(); let container_start_tag = format!("<{}", container_element);
102 let container_end_tag = format!("</{}>", container_element);
103 let container_self_closing = format!("<{}/>", container_element);
104
105 if self.xml.contains(&container_self_closing) {
107 return T::from_choice_variants(Vec::new());
109 }
110
111 let start_pos = if let Some(pos) = self.xml.find(&container_start_tag) {
112 (self.xml[pos..]
115 .find('>')
116 .ok_or_else(|| Error::validation_error("xml", "Malformed container start tag"))?
117 + pos
118 + 1)
119 } else {
120 return Err(Error::validation_error(
121 "xml",
122 &format!("Container element '{}' not found", container_element),
123 ));
124 };
125
126 let end_pos = self
128 .xml
129 .find(&container_end_tag)
130 .ok_or_else(|| Error::validation_error("xml", "Container end tag not found"))?;
131
132 let content = &self.xml[start_pos..end_pos];
133
134 for &element_name in choice_names {
136 let mut search_pos = 0;
137
138 loop {
139 let element_start_tag = format!("<{}", element_name);
140 let element_end_tag = format!("</{}>", element_name);
141 let element_self_closing = format!("<{}/>", element_name);
142 let element_self_closing_with_space = format!("<{} ", element_name);
143
144 let element_pos = if let Some(pos) = content[search_pos..].find(&element_start_tag)
146 {
147 search_pos + pos
148 } else {
149 break; };
151
152 let content_before = &content[search_pos..element_pos];
155 let mut depth_check = 0;
156 let mut check_pos = 0;
157 while check_pos < content_before.len() {
158 if let Some(open_pos) = content_before[check_pos..].find('<') {
159 let abs_open_pos = check_pos + open_pos;
160 if abs_open_pos + 1 < content_before.len() {
161 let tag_start = abs_open_pos + 1;
162 if content_before.chars().nth(tag_start).unwrap() == '/' {
163 depth_check -= 1;
164 } else {
165 if let Some(close_pos) = content_before[abs_open_pos..].find('>') {
167 let tag_content =
168 &content_before[abs_open_pos..abs_open_pos + close_pos + 1];
169 if !tag_content.ends_with("/>") {
170 depth_check += 1;
171 }
172 }
173 }
174 }
175 check_pos = abs_open_pos + 1;
176 } else {
177 break;
178 }
179 }
180
181 if depth_check > 0 {
183 search_pos = element_pos + element_start_tag.len();
184 continue;
185 }
186
187 let is_self_closing = content[element_pos..].starts_with(&element_self_closing)
189 || (content[element_pos..].starts_with(&element_self_closing_with_space)
190 && content[element_pos..].find('>').is_some_and(|pos| {
191 content[element_pos..element_pos + pos + 1].ends_with("/>")
192 }));
193
194 if is_self_closing {
195 let tag_end = content[element_pos..].find('>').unwrap() + element_pos + 1;
196 let element_xml = &content[element_pos..tag_end];
197 let variant = T::parse_choice_element(element_name, element_xml)?;
198 variants.push((element_pos, variant)); search_pos = tag_end;
200 } else {
201 let tag_end_pos = content[element_pos..].find('>').ok_or_else(|| {
203 Error::validation_error("xml", "Malformed element start tag")
204 })? + element_pos
205 + 1;
206
207 let mut depth = 1;
209 let mut current_pos = tag_end_pos;
210 let mut element_end_pos = None;
211
212 while depth > 0 && current_pos < content.len() {
213 if let Some(start_pos) = content[current_pos..].find(&element_start_tag) {
214 let abs_start_pos = current_pos + start_pos;
215 if let Some(end_pos) = content[current_pos..].find(&element_end_tag) {
216 let abs_end_pos = current_pos + end_pos;
217 if abs_start_pos < abs_end_pos {
218 depth += 1;
219 current_pos = abs_start_pos + element_start_tag.len();
220 } else {
221 depth -= 1;
222 if depth == 0 {
223 element_end_pos = Some(abs_end_pos + element_end_tag.len());
224 }
225 current_pos = abs_end_pos + element_end_tag.len();
226 }
227 } else {
228 depth -= 1;
229 if depth == 0 {
230 if let Some(end_tag_pos) =
231 content[current_pos..].find(&element_end_tag)
232 {
233 element_end_pos =
234 Some(current_pos + end_tag_pos + element_end_tag.len());
235 }
236 }
237 break;
238 }
239 } else if let Some(end_pos) = content[current_pos..].find(&element_end_tag)
240 {
241 let abs_end_pos = current_pos + end_pos;
242 depth -= 1;
243 if depth == 0 {
244 element_end_pos = Some(abs_end_pos + element_end_tag.len());
245 }
246 current_pos = abs_end_pos + element_end_tag.len();
247 } else {
248 break;
249 }
250 }
251
252 if let Some(end_pos) = element_end_pos {
253 let element_xml = &content[element_pos..end_pos];
254 let variant = T::parse_choice_element(element_name, element_xml)?;
255 variants.push((element_pos, variant)); search_pos = end_pos;
257 } else {
258 return Err(Error::validation_error(
259 "xml",
260 &format!("Unclosed element '{}'", element_name),
261 ));
262 }
263 }
264 }
265 }
266
267 variants.sort_by_key(|(pos, _)| *pos);
269 let sorted_variants: Vec<T::Variant> =
270 variants.into_iter().map(|(_, variant)| variant).collect();
271
272 T::from_choice_variants(sorted_variants)
273 }
274}
275
276pub struct ChoiceGroupRegistry {
278 }
280
281impl ChoiceGroupRegistry {
282 pub fn new() -> Self {
284 Self {}
285 }
286
287 pub fn parse<T: XsdChoiceGroup>(&self, container_name: &str, xml: &str) -> Result<T> {
289 let parser = ChoiceGroupParser::from_str(xml);
290 parser.parse_choice_group(container_name)
291 }
292}
293
294impl Default for ChoiceGroupRegistry {
295 fn default() -> Self {
296 Self::new()
297 }
298}
299
300static GLOBAL_REGISTRY: std::sync::OnceLock<ChoiceGroupRegistry> = std::sync::OnceLock::new();
302
303pub fn parse_choice_group<T: XsdChoiceGroup>(container_name: &str, xml: &str) -> Result<T> {
305 let registry = GLOBAL_REGISTRY.get_or_init(ChoiceGroupRegistry::new);
306 registry.parse(container_name, xml)
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312
313 #[derive(Debug, PartialEq)]
315 enum TestVariant {
316 ElementA(String),
317 ElementB(i32),
318 ElementC(bool),
319 }
320
321 #[derive(Debug, PartialEq)]
322 struct TestChoiceGroup {
323 elements: Vec<TestVariant>,
324 }
325
326 impl XsdChoiceGroup for TestChoiceGroup {
327 type Variant = TestVariant;
328
329 fn choice_element_names() -> &'static [&'static str] {
330 &["ElementA", "ElementB", "ElementC"]
331 }
332
333 fn parse_choice_element(element_name: &str, xml: &str) -> Result<Self::Variant> {
334 match element_name {
335 "ElementA" => {
336 let start = xml
338 .find('>')
339 .ok_or_else(|| Error::validation_error("xml", "Invalid ElementA"))?;
340 let end = xml
341 .rfind('<')
342 .ok_or_else(|| Error::validation_error("xml", "Invalid ElementA"))?;
343 let content = &xml[start + 1..end];
344 Ok(TestVariant::ElementA(content.to_string()))
345 }
346 "ElementB" => {
347 let start = xml
348 .find('>')
349 .ok_or_else(|| Error::validation_error("xml", "Invalid ElementB"))?;
350 let end = xml
351 .rfind('<')
352 .ok_or_else(|| Error::validation_error("xml", "Invalid ElementB"))?;
353 let content = &xml[start + 1..end];
354 let value = content.parse::<i32>().map_err(|_| {
355 Error::validation_error("xml", "Invalid integer in ElementB")
356 })?;
357 Ok(TestVariant::ElementB(value))
358 }
359 "ElementC" => {
360 let start = xml
361 .find('>')
362 .ok_or_else(|| Error::validation_error("xml", "Invalid ElementC"))?;
363 let end = xml
364 .rfind('<')
365 .ok_or_else(|| Error::validation_error("xml", "Invalid ElementC"))?;
366 let content = &xml[start + 1..end];
367 let value = content.parse::<bool>().map_err(|_| {
368 Error::validation_error("xml", "Invalid boolean in ElementC")
369 })?;
370 Ok(TestVariant::ElementC(value))
371 }
372 _ => Err(Error::validation_error("choice_group", "Unknown element")),
373 }
374 }
375
376 fn from_choice_variants(variants: Vec<Self::Variant>) -> Result<Self> {
377 Ok(TestChoiceGroup { elements: variants })
378 }
379 }
380
381 #[test]
382 fn test_choice_group_trait_system() {
383 assert_eq!(
385 TestChoiceGroup::choice_element_names(),
386 &["ElementA", "ElementB", "ElementC"]
387 );
388
389 let variant_a =
391 TestChoiceGroup::parse_choice_element("ElementA", "<ElementA>test</ElementA>").unwrap();
392 assert_eq!(variant_a, TestVariant::ElementA("test".to_string()));
393
394 let variant_b =
395 TestChoiceGroup::parse_choice_element("ElementB", "<ElementB>42</ElementB>").unwrap();
396 assert_eq!(variant_b, TestVariant::ElementB(42));
397
398 let variant_c =
399 TestChoiceGroup::parse_choice_element("ElementC", "<ElementC>true</ElementC>").unwrap();
400 assert_eq!(variant_c, TestVariant::ElementC(true));
401
402 let variants = vec![variant_a, variant_b, variant_c];
404 let choice_group = TestChoiceGroup::from_choice_variants(variants).unwrap();
405 assert_eq!(choice_group.elements.len(), 3);
406 }
407
408 #[test]
409 fn test_xml_parsing() {
410 let xml = r#"
411 <Container>
412 <ElementA>hello</ElementA>
413 <ElementB>123</ElementB>
414 <ElementC>false</ElementC>
415 </Container>
416 "#;
417
418 let parser = ChoiceGroupParser::from_str(xml);
419 let result: TestChoiceGroup = parser.parse_choice_group("Container").unwrap();
420
421 assert_eq!(result.elements.len(), 3);
422 assert_eq!(
423 result.elements[0],
424 TestVariant::ElementA("hello".to_string())
425 );
426 assert_eq!(result.elements[1], TestVariant::ElementB(123));
427 assert_eq!(result.elements[2], TestVariant::ElementC(false));
428 }
429
430 #[test]
431 fn test_mixed_order_parsing() {
432 let xml = r#"
433 <Container>
434 <ElementC>true</ElementC>
435 <ElementA>world</ElementA>
436 <ElementB>456</ElementB>
437 <ElementA>again</ElementA>
438 </Container>
439 "#;
440
441 let parser = ChoiceGroupParser::from_str(xml);
442 let result: TestChoiceGroup = parser.parse_choice_group("Container").unwrap();
443
444 assert_eq!(result.elements.len(), 4);
445 assert_eq!(result.elements[0], TestVariant::ElementC(true));
446 assert_eq!(
447 result.elements[1],
448 TestVariant::ElementA("world".to_string())
449 );
450 assert_eq!(result.elements[2], TestVariant::ElementB(456));
451 assert_eq!(
452 result.elements[3],
453 TestVariant::ElementA("again".to_string())
454 );
455 }
456
457 #[test]
458 fn test_empty_container() {
459 let xml = r#"<Container></Container>"#;
460
461 let parser = ChoiceGroupParser::from_str(xml);
462 let result: TestChoiceGroup = parser.parse_choice_group("Container").unwrap();
463
464 assert_eq!(result.elements.len(), 0);
465 }
466
467 #[test]
468 fn test_self_closing_container() {
469 let xml = r#"<Container/>"#;
470
471 let parser = ChoiceGroupParser::from_str(xml);
472 let result: TestChoiceGroup = parser.parse_choice_group("Container").unwrap();
473
474 assert_eq!(result.elements.len(), 0);
475 }
476
477 #[test]
478 fn test_container_not_found() {
479 let xml = r#"<WrongContainer><ElementA>test</ElementA></WrongContainer>"#;
480
481 let parser = ChoiceGroupParser::from_str(xml);
482 let result: Result<TestChoiceGroup> = parser.parse_choice_group("Container");
483
484 assert!(result.is_err());
485 assert!(result
486 .unwrap_err()
487 .to_string()
488 .contains("Container element 'Container' not found"));
489 }
490
491 #[test]
492 fn test_global_registry() {
493 let xml = r#"
494 <Container>
495 <ElementA>registry_test</ElementA>
496 <ElementB>999</ElementB>
497 </Container>
498 "#;
499
500 let result: TestChoiceGroup = parse_choice_group("Container", xml).unwrap();
501
502 assert_eq!(result.elements.len(), 2);
503 assert_eq!(
504 result.elements[0],
505 TestVariant::ElementA("registry_test".to_string())
506 );
507 assert_eq!(result.elements[1], TestVariant::ElementB(999));
508 }
509
510 #[test]
511 fn test_registry_instance() {
512 let registry = ChoiceGroupRegistry::new();
513 let xml = r#"
514 <Container>
515 <ElementC>false</ElementC>
516 </Container>
517 "#;
518
519 let result: TestChoiceGroup = registry.parse("Container", xml).unwrap();
520
521 assert_eq!(result.elements.len(), 1);
522 assert_eq!(result.elements[0], TestVariant::ElementC(false));
523 }
524}