1use quick_xml::Reader;
2
3use crate::{
6 detail::{parse, CotUnparsedDetail},
7 Error,
8};
9
10#[derive(Copy, Clone, Debug, PartialEq)]
12pub enum TakCotType {
13 GeoFence,
14 Marker,
15 RangeBearing,
16 Route,
17 Shape,
18 Other,
19}
20
21pub struct TakCotMessage {
27 pub cot_type: TakCotType,
28 pub cot_msg: CotUnparsedDetail,
29}
30
31pub fn detect_tak_cot_type(input: &str) -> Result<TakCotMessage, Error> {
36 let cot_msg = parse(input)?;
37 let type_tokens = [
41 ("u-r-b-", TakCotType::RangeBearing),
42 ("u-rb-", TakCotType::RangeBearing),
43 ("b-m-r", TakCotType::Route),
44 ("u-d-", TakCotType::Shape),
45 ];
46 let detail_tokens = [
47 ("__geofence", TakCotType::GeoFence),
48 ("usericon", TakCotType::Marker),
49 ];
50 for line in &cot_msg.detail {
52 for (search, msg_type) in detail_tokens.iter() {
53 if line.contains(search) {
54 return Ok(TakCotMessage {
55 cot_type: *msg_type,
56 cot_msg,
57 });
58 }
59 }
60 }
61 for (search, msg_type) in type_tokens.iter() {
63 if cot_msg.cot_type.contains(search) {
64 return Ok(TakCotMessage {
65 cot_type: *msg_type,
66 cot_msg,
67 });
68 }
69 }
70 Ok(TakCotMessage {
71 cot_type: TakCotType::Other,
72 cot_msg,
73 })
74}
75
76pub fn parse_cot_msg_type(text: &str) -> Result<String, Error> {
78 match xml_first_element_w_attr(text, "event", "type") {
79 Ok(Some(val)) => Ok(val),
80 _ => Err(Error::BadField("No element 'event' with attribute 'type'")),
81 }
82}
83
84pub fn xml_first_element_w_attr(
86 text: &str,
87 elt_name: &str,
88 attr_name: &str,
89) -> Result<Option<String>, Error> {
90 let mut reader = Reader::from_str(text);
91 reader.config_mut().trim_text(true);
92 loop {
93 match reader.read_event()? {
94 quick_xml::events::Event::Start(ref e) => {
96 if e.name().into_inner() == elt_name.as_bytes() {
97 for attr in e.attributes() {
98 let attr = attr?;
99 if attr.key.into_inner() == attr_name.as_bytes() {
100 return Ok(Some(String::from_utf8_lossy(&attr.value).to_string()));
101 }
102 }
103 }
104 }
105 quick_xml::events::Event::Eof => break,
106 _ => {}
107 }
108 }
109 Ok(None)
110}
111
112#[cfg(test)]
113mod test {
114 use crate::tak::test::get_xml_examples;
115
116 use super::{detect_tak_cot_type, TakCotType};
117
118 #[test]
119 fn test_tak_cot_detect() {
120 let examples = get_xml_examples().unwrap();
121 for res in examples {
122 let (filename, cot_xml) = res.unwrap();
123 let cot = detect_tak_cot_type(&cot_xml).unwrap();
124 if filename.starts_with("geo-fence") {
125 assert_type(&filename, cot.cot_type, TakCotType::GeoFence);
126 } else if filename.starts_with("marker-") {
127 assert_type(&filename, cot.cot_type, TakCotType::Marker);
128 } else if filename.starts_with("range-bearing") {
129 assert_type(&filename, cot.cot_type, TakCotType::RangeBearing);
130 } else if filename.starts_with("route") {
131 assert_type(&filename, cot.cot_type, TakCotType::Route);
132 } else if filename.starts_with("shape-") {
133 assert_type(&filename, cot.cot_type, TakCotType::Shape);
134 }
135 }
136 }
137
138 fn assert_type(filename: &str, actual: TakCotType, expected: TakCotType) {
139 assert_eq!(
140 actual, expected,
141 "{}: expected {:?}, got {:?}",
142 filename, expected, actual
143 );
144 }
145}