roslibrust_codegen/parse/
msg.rs

1use crate::parse::{parse_constant_field, parse_field, strip_comments};
2use crate::Error;
3use crate::{ConstantInfo, FieldInfo, Package, RosVersion};
4use std::path::{Path, PathBuf};
5
6/// Describes all information for a single message file
7#[derive(Clone, PartialEq, Debug)]
8pub struct ParsedMessageFile {
9    pub name: String,
10    pub package: String,
11    pub fields: Vec<FieldInfo>,
12    pub constants: Vec<ConstantInfo>,
13    pub version: Option<RosVersion>,
14    /// The contents of the message file this instance was parsed from
15    pub source: String,
16    /// The path where the message was found
17    pub path: PathBuf,
18}
19
20impl ParsedMessageFile {
21    pub fn has_header(&self) -> bool {
22        self.fields.iter().any(|field| {
23            field.field_type.field_type.as_str() == "Header"
24                && (field.field_type.package_name.is_none()
25                    || field.field_type.package_name == Some(String::from("std_msgs")))
26        })
27    }
28
29    pub fn get_full_name(&self) -> String {
30        format!("{}/{}", self.package, self.name)
31    }
32}
33
34/// Converts a ros message file into a struct representation
35/// * `data` -- Raw contents of the file as loaded from disk
36/// * `name` -- Name of the object being parsed excluding the file extension, e.g. `Header`
37/// * `package` -- Name of the package the message is found in, required for relative type paths
38/// * `ros2` -- True iff the package is a ros2 package and should be parsed with ros2 logic
39pub fn parse_ros_message_file(
40    data: &str,
41    name: &str,
42    package: &Package,
43    path: &Path,
44) -> Result<ParsedMessageFile, Error> {
45    let mut fields = vec![];
46    let mut constants = vec![];
47
48    for line in data.lines() {
49        let line = strip_comments(line).trim();
50        if line.is_empty() {
51            // Comment only line skip
52            continue;
53        }
54        // Determine if we're looking at a constant or a field
55        let sep = line.find(' ').ok_or(
56            Error::new(
57                format!("Found an invalid ros field line, no space delinting type from name: {line} in {}\n{data}",
58                path.display())
59            )
60        )?;
61        let equal_after_sep = line[sep..].find('=');
62        if equal_after_sep.is_some() {
63            // Since we found an equal sign after a space, this must be a constant
64            constants.push(parse_constant_field(line, package)?)
65        } else {
66            // Is regular field
67            fields.push(parse_field(line, package, name)?);
68        }
69    }
70    Ok(ParsedMessageFile {
71        fields,
72        constants,
73        name: name.to_owned(),
74        package: package.name.clone(),
75        version: package.version,
76        source: data.to_owned(),
77        path: path.to_owned(),
78    })
79}