use std::collections::HashMap;
use std::fs;
use std::time::Instant;
use quick_xml::reader::Reader;
use crate::protocol::{Dialect, Protocol};
use super::errors::XmlParseError;
use super::xml::XmlParser;
use super::xml_definition::MavLinkXmlDialectDefinition;
#[derive(Debug)]
pub struct MAVInspector {
src: String,
definitions: Vec<MavLinkXmlDialectDefinition>,
}
impl MAVInspector {
pub fn new(src: &str) -> std::io::Result<Self> {
Ok(MAVInspector {
src: src.to_string(),
definitions: Self::discover_definitions(src)?,
})
}
pub fn src(&self) -> &String {
&self.src
}
pub fn definitions(&self) -> &Vec<MavLinkXmlDialectDefinition> {
&self.definitions
}
pub fn discover_definitions(path: &str) -> std::io::Result<Vec<MavLinkXmlDialectDefinition>> {
let mut dialects: Vec<MavLinkXmlDialectDefinition> = Vec::new();
for entry in fs::read_dir(path)? {
let entry_path = entry?.path();
if entry_path.is_file()
&& entry_path
.extension()
.unwrap()
.to_str()
.unwrap()
.to_lowercase()
.eq("xml")
{
let path = entry_path.to_str().unwrap().to_string();
dialects.push(MavLinkXmlDialectDefinition::new(&path));
}
}
Ok(dialects)
}
pub fn parse(&self) -> Result<Protocol, XmlParseError> {
let mut dialects: HashMap<String, Dialect> = HashMap::new();
println!("\n\rDialects:");
let started_at = Instant::now();
for def in &self.definitions {
if dialects.contains_key(def.name()) {
break;
}
Self::parse_definition(def, &mut dialects)?;
}
let ended_at = Instant::now();
let duration = ended_at - started_at;
println!("\n\rParsed:");
println!("\tdialects: {:?}", dialects.keys());
println!("\tduration: {}s", (duration.as_micros() as f64) / 1000000.0);
Ok(Protocol::new(dialects))
}
pub fn parse_definition<'a>(
definition: &MavLinkXmlDialectDefinition,
dialects: &'a mut HashMap<String, Dialect>,
) -> Result<&'a Dialect, XmlParseError> {
if dialects.contains_key(definition.name()) {
return Ok(dialects.get(definition.name()).unwrap());
}
for dependency in definition.includes() {
Self::parse_definition(dependency, dialects)?;
}
let mut enums = HashMap::new();
let mut messages = HashMap::new();
for dependency in definition.includes() {
for (name, enm) in dialects.get(dependency.name()).unwrap().enums() {
enums.insert(name.clone(), enm.clone());
}
}
for dependency in definition.includes() {
for (&id, message) in dialects.get(dependency.name()).unwrap().messages() {
messages.insert(id, message.clone());
}
}
let started_at = Instant::now();
let mut parser: XmlParser = XmlParser::new(&mut enums, &mut messages);
let mut file_reader = Reader::from_file(definition.path()).unwrap();
parser.parse(definition.name(), &mut file_reader)?;
dialects.insert(
definition.name().clone(),
Dialect::new(
definition.name().clone(),
definition.version(),
definition.dialect(),
messages,
enums,
),
);
let ended_at = Instant::now();
let duration = ended_at - started_at;
println!("\r\t{}", definition.name());
println!("\t\tpath: {}", definition.path());
println!("\t\tversion: {:?}", definition.version());
println!("\t\tdialect #: {:?}", definition.dialect());
println!(
"\t\tduration: {}s",
(duration.as_micros() as f64) / 1000000.0
);
Ok(dialects.get(definition.name()).unwrap())
}
}
#[cfg(test)]
mod tests {
use super::*;
const DEFAULT_DIALECTS_PATH: &str = "./mavlink/message_definitions/v1.0";
#[test]
fn dialects_are_available() {
let parser = MAVInspector::new(DEFAULT_DIALECTS_PATH).unwrap();
assert!(!parser.definitions().is_empty())
}
}