Skip to main content

roslibrust_codegen/parse/
action.rs

1use crate::parse::ParsedMessageFile;
2use crate::{bail, Error, Package};
3use std::path::{Path, PathBuf};
4
5use super::parse_ros_message_file;
6
7#[derive(Clone, Debug)]
8pub struct ParsedActionFile {
9    pub name: String,
10    pub package: String,
11    pub action_type: ParsedMessageFile,
12    pub goal_type: ParsedMessageFile,
13    pub result_type: ParsedMessageFile,
14    pub feedback_type: ParsedMessageFile,
15    pub action_goal_type: ParsedMessageFile,
16    pub action_result_type: ParsedMessageFile,
17    pub action_feedback_type: ParsedMessageFile,
18    /// The contents of the action file this instance was parsed from
19    pub source: String,
20    /// The path where the message was found
21    pub path: PathBuf,
22}
23
24pub fn parse_ros_action_file(
25    data: &str,
26    name: &str,
27    package: &Package,
28    path: &Path,
29) -> Result<ParsedActionFile, Error> {
30    let mut dash_line_number_1 = None;
31    let mut dash_line_number_2 = None;
32
33    for (line_num, line) in data.lines().enumerate() {
34        match (line.find("---"), line.find('#')) {
35            (Some(dash_idx), Some(comment_idx)) => {
36                if dash_idx < comment_idx {
37                    if dash_line_number_1.is_none() {
38                        dash_line_number_1 = Some(line_num);
39                    } else {
40                        dash_line_number_2 = Some(line_num);
41                        break;
42                    }
43                }
44            }
45            (Some(_), None) => {
46                if dash_line_number_1.is_none() {
47                    dash_line_number_1 = Some(line_num);
48                } else {
49                    dash_line_number_2 = Some(line_num);
50                    break;
51                }
52            }
53            _ => continue,
54        }
55    }
56    let str_accumulator = |mut acc: String, line: &str| -> String {
57        acc.push_str(line);
58        acc.push('\n');
59        acc
60    };
61
62    if let (Some(first_dash_line), Some(second_dash_line)) =
63        (dash_line_number_1, dash_line_number_2)
64    {
65        let goal_str = data
66            .lines()
67            .take(first_dash_line)
68            .fold(String::new(), str_accumulator);
69        let result_str = data
70            .lines()
71            .skip(first_dash_line + 1)
72            .take(second_dash_line - first_dash_line - 1)
73            .fold(String::new(), str_accumulator);
74        let feedback_str = data
75            .lines()
76            .skip(second_dash_line + 1)
77            .fold(String::new(), str_accumulator);
78
79        Ok(ParsedActionFile {
80            name: name.to_owned(),
81            package: package.name.clone(),
82            action_type: generate_action_msg(name, package, path)?,
83            goal_type: parse_ros_message_file(
84                &goal_str,
85                format!("{name}Goal").as_str(),
86                package,
87                path,
88            )?,
89            result_type: parse_ros_message_file(
90                &result_str,
91                format!("{name}Result").as_str(),
92                package,
93                path,
94            )?,
95            feedback_type: parse_ros_message_file(
96                &feedback_str,
97                format!("{name}Feedback").as_str(),
98                package,
99                path,
100            )?,
101            action_goal_type: generate_action_goal_msg(name, package, path)?,
102            action_result_type: generate_action_result_msg(name, package, path)?,
103            action_feedback_type: generate_action_feedback_msg(name, package, path)?,
104            source: data.to_owned(),
105            path: path.to_owned(),
106        })
107    } else {
108        bail!(
109            "Failed to find both expected delimiter lines '---' in {}/{name}",
110            &package.name
111        )
112    }
113}
114
115fn generate_action_msg(
116    name: &str,
117    package: &Package,
118    path: &Path,
119) -> Result<ParsedMessageFile, Error> {
120    let source = format!(
121        r#"
122{name}ActionGoal action_goal
123{name}ActionResult action_result
124{name}ActionFeedback action_feedback
125    "#
126    );
127
128    parse_ros_message_file(&source, format!("{name}Action").as_str(), package, path)
129}
130
131fn generate_action_goal_msg(
132    name: &str,
133    package: &Package,
134    path: &Path,
135) -> Result<ParsedMessageFile, Error> {
136    let source = format!(
137        r#"
138Header header
139actionlib_msgs/GoalID goal_id
140{name}Goal goal
141    "#
142    );
143
144    parse_ros_message_file(&source, format!("{name}ActionGoal").as_str(), package, path)
145}
146
147fn generate_action_result_msg(
148    name: &str,
149    package: &Package,
150    path: &Path,
151) -> Result<ParsedMessageFile, Error> {
152    let source = format!(
153        r#"
154Header header
155actionlib_msgs/GoalStatus status
156{name}Result result
157        "#
158    );
159
160    parse_ros_message_file(
161        &source,
162        format!("{name}ActionResult").as_str(),
163        package,
164        path,
165    )
166}
167
168fn generate_action_feedback_msg(
169    name: &str,
170    package: &Package,
171    path: &Path,
172) -> Result<ParsedMessageFile, Error> {
173    let source = format!(
174        r#"
175Header header
176actionlib_msgs/GoalStatus status
177{name}Feedback feedback
178        "#
179    );
180
181    parse_ros_message_file(
182        &source,
183        format!("{name}ActionFeedback").as_str(),
184        package,
185        path,
186    )
187}