1use crate::base::serialize_date;
2use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4
5use crate::base::deserialize_date;
6
7#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
16pub struct TakMarkerDetail {
17 pub status: Status,
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub link: Option<Link>,
20 pub contact: Contact,
21 #[serde(skip_serializing_if = "Option::is_none")]
22 pub remarks: Option<Remarks>,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 pub color: Option<Color>,
25 pub precisionlocation: PrecisionLocation,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub usericon: Option<UserIcon>,
28}
29
30#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
32pub struct Status {
33 #[serde(rename = "@readiness")]
34 pub readiness: bool,
35}
36impl Default for Status {
37 fn default() -> Self {
38 Status { readiness: true }
39 }
40}
41
42#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
43pub struct Link {
44 #[serde(rename = "@uid")]
45 uid: String,
46 #[serde(
47 rename = "@production_time",
48 serialize_with = "serialize_date",
49 deserialize_with = "deserialize_date"
50 )]
51 pub production_time: DateTime<Utc>,
52 #[serde(rename = "@type")]
53 pub cot_type: String,
54 #[serde(rename = "@parent_callsign")]
55 pub parent_callsign: String,
56 #[serde(rename = "@relation")]
57 pub relation: String,
58}
59
60#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
61pub struct Contact {
62 #[serde(rename = "@callsign")]
63 pub callsign: String,
64 #[serde(rename = "@emailAddress", skip_serializing_if = "Option::is_none")]
65 pub email_address: Option<String>,
66 #[serde(rename = "@endpoint", skip_serializing_if = "Option::is_none")]
67 pub endpoint: Option<String>,
68 #[serde(rename = "@phone", skip_serializing_if = "Option::is_none")]
69 pub phone: Option<u32>,
70 #[serde(rename = "@xmppUsername", skip_serializing_if = "Option::is_none")]
71 pub xmpp_username: Option<String>,
72}
73impl Default for Contact {
74 fn default() -> Self {
75 Contact {
76 callsign: "???".to_string(),
77 email_address: None,
78 endpoint: None,
79 phone: None,
80 xmpp_username: None,
81 }
82 }
83}
84
85#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
86pub struct Remarks {
87 #[serde(rename = "$value")]
89 pub source: Option<Vec<String>>,
90}
91
92#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
93pub struct Color {
94 #[serde(rename = "@argb")]
95 pub argb: i32,
96}
97
98#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
99pub struct PrecisionLocation {
100 #[serde(rename = "@altsrc")]
101 pub altsrc: String,
102 #[serde(rename = "@geopointsrc", skip_serializing_if = "Option::is_none")]
103 pub geopointsrc: Option<String>,
104 #[serde(
105 rename = "@PRECISE_IMAGE_FILE",
106 skip_serializing_if = "Option::is_none"
107 )]
108 pub pi_file: Option<String>,
109 #[serde(
110 rename = "@PRECISE_IMAGE_FILE_X",
111 skip_serializing_if = "Option::is_none"
112 )]
113 pub pi_file_x: Option<String>,
114 #[serde(
115 rename = "@PRECISE_IMAGE_FILE_Y",
116 skip_serializing_if = "Option::is_none"
117 )]
118 pub pi_file_y: Option<String>,
119}
120
121impl Default for PrecisionLocation {
122 fn default() -> Self {
123 Self {
124 altsrc: "???".to_string(),
125 geopointsrc: None,
126 pi_file: None,
127 pi_file_x: None,
128 pi_file_y: None,
129 }
130 }
131}
132
133#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
134pub struct UserIcon {
135 #[serde(rename = "@iconsetpath")]
136 iconsetpath: String,
137}
138
139#[cfg(test)]
140mod test {
141 use crate::base::Cot;
142
143 use super::*;
144 #[test]
145 fn test_deserialize_tak_marker() {
146 let xml_path = format!(
147 "{}/src/tak/examples/marker-2525.cot",
148 env!("CARGO_MANIFEST_DIR")
149 );
150 let xml_text = std::fs::read_to_string(&xml_path).unwrap();
151 let marker: Cot<TakMarkerDetail> = quick_xml::de::from_str(&xml_text).unwrap();
152 assert_eq!(marker.version, "2.0");
153 }
154}