use std::convert::TryFrom; use std::io::BufReader; use crate::DsnRespParseError; use xml::attribute::OwnedAttribute; use xml::reader::XmlEvent; use xml::EventReader; fn get_attr(attrs: &[OwnedAttribute], name: &str) -> Result { attrs .iter() .find_map(|o| { if o.name.local_name.eq(name) { Some(o.value.clone()) } else { None } }) .ok_or_else(|| DsnRespParseError::AttrMissing(name.to_string())) } #[derive(Clone, Debug)] pub struct DsnResponse { pub stations: Vec, } impl DsnResponse { pub fn get_active_targets(&self) -> Vec { let mut targets = Vec::new(); for station in &self.stations { for dish in &station.dishes { targets.append(&mut dish.target.clone()) } } targets } pub fn from_xml_string(resp_str: &str) -> Result { Self::from_xml_response(BufReader::new(resp_str.as_bytes())) } pub fn from_xml_response(reader: BufReader) -> Result where T: std::io::Read, { let mut stations = Vec::new(); let mut station: Option = None; let mut dish: Option = None; let parser = EventReader::new(reader).into_iter(); for e in parser { match e { Ok(XmlEvent::StartElement { name, attributes, .. }) => { if name.local_name.contains("station") { if let Some(station) = station { stations.push(station); } station = Some(Station::try_from(attributes).unwrap()); } else if name.local_name.contains("dish") { dish = Some(Dish::try_from(attributes).unwrap()); } else if name.local_name.contains("downSignal") { dish = Some( dish.unwrap() .add_down_signal(Signal::try_from(attributes).unwrap()), ); } else if name.local_name.contains("upSignal") { dish = Some( dish.unwrap() .add_up_signal(Signal::try_from(attributes).unwrap()), ); } else if name.local_name.contains("target") { dish = Some( dish.unwrap() .add_target(Target::try_from(attributes).unwrap()), ); } } Ok(XmlEvent::EndElement { name }) => { if name.local_name.contains("dish") && station.is_some() && dish.is_some() { station = Some(station.unwrap().add_dish(dish.unwrap())); dish = None; } } Ok(XmlEvent::EndDocument) => { if let Some(station) = station { stations.push(station); break; } } Err(e) => { return Err(DsnRespParseError::from(e)); } _ => {} } } Ok(DsnResponse { stations }) } } #[derive(Clone, Debug)] pub struct Station { pub name: String, pub friendly_name: String, pub time_utc: u64, pub tz_offset: i64, pub dishes: Vec, } impl Station { pub fn add_dish(mut self, dish: Dish) -> Self { self.dishes.push(dish); self } } impl TryFrom> for Station { type Error = DsnRespParseError; fn try_from(attrs: Vec) -> Result { let name = get_attr(&attrs, "name")?; let friendly_name = get_attr(&attrs, "friendlyName")?; let time_utc = get_attr(&attrs, "timeUTC")?.parse::()?; let tz_offset = get_attr(&attrs, "timeZoneOffset")?.parse::()?; Ok(Self { name, friendly_name, time_utc, tz_offset, dishes: Vec::new(), }) } } #[derive(Clone, Debug, Eq, PartialEq)] pub enum SignalType { Data, Carrier, None, Unkown(String), } impl From for SignalType { fn from(s: String) -> Self { match s.to_lowercase().as_str() { "data" => SignalType::Data, "carrier" => SignalType::Carrier, "none" => SignalType::None, _ => SignalType::Unkown(s), } } } impl Default for SignalType { fn default() -> Self { SignalType::None } } #[derive(Clone, Debug, Default)] pub struct Signal { pub signal_type: SignalType, pub data_rate: Option, pub frequency: Option, pub power: Option, pub spacecraft: String, pub spacecraft_id: Option, } impl TryFrom> for Signal { type Error = DsnRespParseError; fn try_from(attrs: Vec) -> Result { let signal_type = SignalType::from(get_attr(&attrs, "signalType")?); let data_rate = get_attr(&attrs, "dataRate")?.parse::().ok(); let frequency = get_attr(&attrs, "frequency")?.parse::().ok(); let power = get_attr(&attrs, "power")?.parse::().ok(); let spacecraft = get_attr(&attrs, "spacecraft")?; let spacecraft_id = get_attr(&attrs, "spacecraftID")?.parse::().ok(); Ok(Self { signal_type, data_rate, frequency, power, spacecraft, spacecraft_id, }) } } #[derive(Clone, Debug, Default)] pub struct Target { pub name: String, pub id: u32, /// Up Leg Range (km) pub upleg_range: f64, /// Down Leg Leg Range (km) pub downleg_range: f64, /// Round trip light time (s) pub rtlt: f64, } impl TryFrom> for Target { type Error = DsnRespParseError; fn try_from(attrs: Vec) -> Result { let name = get_attr(&attrs, "name")?; let id = get_attr(&attrs, "id")?.parse::()?; let upleg_range = get_attr(&attrs, "uplegRange")?.parse::()?; let downleg_range = get_attr(&attrs, "downlegRange")?.parse::()?; let rtlt = get_attr(&attrs, "rtlt")?.parse::()?; Ok(Self { name, id, upleg_range, downleg_range, rtlt, }) } } #[derive(Clone, Debug)] pub struct Dish { pub name: String, pub azimuth_angle: Option, pub elevation_angle: Option, pub wind_speed: Option, pub is_mspa: bool, pub is_array: bool, pub is_ddor: bool, pub up_signal: Vec, pub down_signal: Vec, pub target: Vec, } impl Dish { pub fn add_up_signal(mut self, signal: Signal) -> Self { self.up_signal.push(signal); self } pub fn add_down_signal(mut self, signal: Signal) -> Self { self.down_signal.push(signal); self } pub fn add_target(mut self, target: Target) -> Self { self.target.push(target); self } } impl TryFrom> for Dish { type Error = DsnRespParseError; fn try_from(attrs: Vec) -> Result { let name = get_attr(&attrs, "name")?; let azimuth_angle = get_attr(&attrs, "azimuthAngle")?.parse::().ok(); let elevation_angle = get_attr(&attrs, "elevationAngle")?.parse::().ok(); let wind_speed = get_attr(&attrs, "windSpeed")?.parse::().ok(); let is_mspa = get_attr(&attrs, "isMSPA")?.parse::()?; let is_array = get_attr(&attrs, "isArray")?.parse::()?; let is_ddor = get_attr(&attrs, "isDDOR")?.parse::()?; Ok(Self { name, azimuth_angle, elevation_angle, wind_speed, is_mspa, is_array, is_ddor, up_signal: Vec::new(), down_signal: Vec::new(), target: Vec::new(), }) } }