use arma_rs::{FromArma, FromArmaError}; #[derive(Clone)] struct DrawPoint { lat: f64, lon: f64, hae: f32, } pub struct DrawEllipsePayload { pub uuid: String, pub cot_type: String, pub center_lat: f64, pub center_lon: f64, pub center_hae: f32, pub major: f64, pub minor: f64, pub angle: f32, pub callsign: String, pub stale_seconds: i64, pub stroke_color: i32, pub fill_color: i32, pub stroke_weight: f64, pub milsym: String, } pub struct DrawLinksPayload { pub uuid: String, pub cot_type: String, pub center_lat: f64, pub center_lon: f64, pub center_hae: f32, pub points: String, pub callsign: String, pub stale_seconds: i64, pub stroke_color: i32, pub fill_color: i32, pub stroke_weight: f64, pub stroke_style: String, pub closed: bool, pub milsym: String, } impl FromArma for DrawEllipsePayload { fn from_arma(data: String) -> Result { let ( uuid, cot_type, center_lat, center_lon, center_hae, major, minor, angle, callsign, stale_seconds, stroke_color, fill_color, stroke_weight, milsym, ) = <( String, String, f64, f64, f32, f64, f64, f32, String, i64, i32, i32, f64, String, )>::from_arma(data)?; Ok(Self { uuid, cot_type, center_lat, center_lon, center_hae, major, minor, angle, callsign, stale_seconds, stroke_color, fill_color, stroke_weight, milsym, }) } } impl FromArma for DrawLinksPayload { fn from_arma(data: String) -> Result { let ( uuid, cot_type, center_lat, center_lon, center_hae, points, callsign, stale_seconds, stroke_color, fill_color, stroke_weight, stroke_style, closed, milsym, ) = <( String, String, f64, f64, f32, String, String, i64, i32, i32, f64, String, bool, String, )>::from_arma(data)?; Ok(Self { uuid, cot_type, center_lat, center_lon, center_hae, points, callsign, stale_seconds, stroke_color, fill_color, stroke_weight, stroke_style, closed, milsym, }) } } impl DrawEllipsePayload { pub fn to_xml(&self, now: &str, stale: &str) -> String { shape_event( &self.uuid, &self.cot_type, self.center_lat, self.center_lon, self.center_hae, now, stale, &self.callsign, &ellipse_shape_detail( &self.uuid, self.major, self.minor, self.angle, self.stroke_color, self.fill_color, self.stroke_weight, ), self.stroke_color, self.fill_color, self.stroke_weight, "solid", &self.milsym, ) } } impl DrawLinksPayload { pub fn to_xml(&self, now: &str, stale: &str) -> String { let points = parse_points(&self.points); let shape_detail = links_detail(&points, self.closed); shape_event( &self.uuid, &self.cot_type, self.center_lat, self.center_lon, self.center_hae, now, stale, &self.callsign, &shape_detail, self.stroke_color, self.fill_color, self.stroke_weight, &self.stroke_style, &self.milsym, ) } } fn shape_event( uid: &str, cot_type: &str, lat: f64, lon: f64, hae: f32, now: &str, stale: &str, callsign: &str, shape_detail: &str, stroke_color: i32, fill_color: i32, stroke_weight: f64, stroke_style: &str, milsym: &str, ) -> String { let milsym_detail = if milsym.trim().is_empty() { String::new() } else { format!(r#"<__milsym id="{}" />"#, escape_attr(milsym)) }; format!( r#"{shape_detail}<__shapeExtras cpvis="true" editable="true" />{milsym_detail}"#, uid = escape_attr(uid), cot_type = escape_attr(cot_type), now = now, stale = stale, lat = lat, lon = lon, hae = hae, shape_detail = shape_detail, callsign = escape_attr(callsign), stroke_color = stroke_color, stroke_weight = stroke_weight, stroke_style = escape_attr(stroke_style), fill_color = fill_color, milsym_detail = milsym_detail ) } fn ellipse_shape_detail( uid: &str, major: f64, minor: f64, angle: f32, stroke_color: i32, fill_color: i32, stroke_weight: f64, ) -> String { format!( r#""#, major = major, minor = minor, angle = angle, style_uid = escape_attr(&format!("{}.Style", uid)), stroke_hex = argb_hex(stroke_color), stroke_weight = stroke_weight, fill_hex = argb_hex(fill_color) ) } fn links_detail(points: &[DrawPoint], closed: bool) -> String { let mut detail = String::new(); for point in points { detail.push_str(&link_detail(point)); } if closed { if let Some(first) = points.first() { detail.push_str(&link_detail(first)); } } detail } fn link_detail(point: &DrawPoint) -> String { format!( r#""#, lat = point.lat, lon = point.lon, hae = point.hae ) } fn parse_points(raw: &str) -> Vec { raw.split(';') .filter_map(|entry| { let parts: Vec<_> = entry.split(',').collect(); if parts.len() != 3 { return None; } Some(DrawPoint { lat: parts[0].parse().ok()?, lon: parts[1].parse().ok()?, hae: parts[2].parse().ok()?, }) }) .collect() } fn argb_hex(color: i32) -> String { format!("{:08x}", color as u32) } fn escape_attr(value: &str) -> String { value .replace('&', "&") .replace('"', """) .replace('<', "<") .replace('>', ">") }