1use quick_xml::events::Event;
2
3use crate::{
4 base::{Cot, CotBase},
5 Error,
6};
7
8pub type CotUnparsedDetail = Cot<Vec<String>>;
11
12impl From<CotBase> for CotUnparsedDetail {
13 fn from(cot: CotBase) -> Self {
14 CotUnparsedDetail {
15 version: cot.version,
16 uid: cot.uid,
17 cot_type: cot.cot_type,
18 time: cot.time,
19 start: cot.start,
20 stale: cot.stale,
21 how: cot.how,
22 detail: vec![],
23 point: cot.point,
24 }
25 }
26}
27
28pub fn parse(input: &str) -> Result<CotUnparsedDetail, Error> {
42 let mut reader = quick_xml::Reader::from_str(input);
43 reader.config_mut().trim_text(true);
44 let detail = extract_detail(reader)?;
45 let cot_base: CotBase = quick_xml::de::from_str(input)?;
46 let mut cot: CotUnparsedDetail = cot_base.into();
47 cot.detail = detail;
48 Ok(cot)
49}
50
51pub fn extract_detail(mut reader: quick_xml::reader::Reader<&[u8]>) -> Result<Vec<String>, Error> {
54 let mut detail: Vec<String> = vec![];
55 let mut is_detail = false;
56 loop {
57 match reader.read_event() {
58 Ok(Event::Start(ref e)) => {
59 if e.name().as_ref() == b"detail" {
60 is_detail = true;
61 }
62 }
63 Ok(Event::Empty(ref e)) => {
64 if is_detail {
65 detail.push(format!("<{}/>", String::from_utf8_lossy(e)));
67 }
68 }
69 Ok(Event::Text(_e)) => {}
70 Ok(Event::End(ref e)) => {
71 if e.name().as_ref() == b"detail" {
72 is_detail = false;
73 }
74 }
75 Err(e) => return Err(Error::Xml(e)),
76 Ok(Event::Eof) => break,
77 _ => (),
78 }
79 }
80 Ok(detail)
81}
82
83#[cfg(test)]
84mod test {
85 use std::collections::HashSet;
86
87 use crate::examples::{
88 COT_STRIKE_DETAIL_LINES, COT_STRIKE_EXAMPLE, COT_TRACK_DETAIL_LINES, COT_TRACK_EXAMPLE,
89 };
90
91 #[test]
92 fn test_detail_parse_track2() {
93 test_expected_detail(COT_TRACK_EXAMPLE, &COT_TRACK_DETAIL_LINES)
94 }
95
96 #[test]
97 fn test_detail_parse_strike() {
98 test_expected_detail(COT_STRIKE_EXAMPLE, &COT_STRIKE_DETAIL_LINES)
99 }
100
101 fn test_expected_detail(input: &str, expected_lines: &[&str]) {
102 let cot = super::parse(input).unwrap();
103 let mut expected_lines: HashSet<&str> = HashSet::from_iter(expected_lines.iter().cloned());
104 for line in &cot.detail {
105 let removed = expected_lines.remove(&line.as_str());
106 if !removed {
107 panic!(
108 "Unexpected line: {:?}\n not in: {:?}",
109 line, expected_lines
110 );
111 }
112 }
113 assert_eq!(expected_lines.len(), 0);
114 }
115}