use crate::parse::{parse_constant_field, parse_field, strip_comments};
use crate::Error;
use crate::{ConstantInfo, FieldInfo, Package, RosVersion};
use std::path::{Path, PathBuf};
#[derive(Clone, PartialEq, Debug)]
pub struct ParsedMessageFile {
pub name: String,
pub package: String,
pub fields: Vec<FieldInfo>,
pub constants: Vec<ConstantInfo>,
pub version: Option<RosVersion>,
pub source: String,
pub path: PathBuf,
}
impl ParsedMessageFile {
pub fn has_header(&self) -> bool {
self.fields.iter().any(|field| {
field.field_type.field_type.as_str() == "Header"
&& (field.field_type.package_name.is_none()
|| field.field_type.package_name == Some(String::from("std_msgs")))
})
}
pub fn get_full_name(&self) -> String {
format!("{}/{}", self.package, self.name)
}
pub fn get_ros2_full_name(&self) -> String {
let parent = self
.path
.parent()
.expect("All ROS messages should have a parent directory")
.file_name()
.expect("All parent directories should have a resolveable file name")
.to_str()
.expect("All parent directory names should be valid unicode");
format!("{}/{}/{}", self.package, parent, self.name)
}
pub fn get_ros2_dds_type_name(&self) -> String {
format!("{}::msg::dds_::{}_", self.package, self.name)
}
}
pub fn parse_ros_message_file(
data: &str,
name: &str,
package: &Package,
path: &Path,
) -> Result<ParsedMessageFile, Error> {
let mut fields = vec![];
let mut constants = vec![];
for line in data.lines() {
let line = strip_comments(line).trim();
if line.is_empty() {
continue;
}
let sep = line.find(' ').ok_or(
Error::new(
format!("Found an invalid ros field line, no space delinting type from name: {line} in {}\n{data}",
path.display())
)
)?;
let equal_after_sep = line[sep..].find('=');
if equal_after_sep.is_some() {
constants.push(parse_constant_field(line, package)?)
} else {
fields.push(parse_field(line, package, name)?);
}
}
Ok(ParsedMessageFile {
fields,
constants,
name: name.to_owned(),
package: package.name.clone(),
version: package.version,
source: data.to_owned(),
path: path.to_owned(),
})
}