ros2msg/lib.rs
1#![deny(
2 unsafe_code,
3 unused_must_use,
4 unreachable_pub,
5 rust_2018_idioms,
6 missing_docs,
7 clippy::pedantic
8)]
9
10//! # ROS2 Message Parser
11//!
12//! A comprehensive Rust library for parsing ROS2 message, service, and action files.
13//! This crate provides functionality to parse `.msg`, `.srv`, and `.action` files
14//! according to the ROS2 IDL specification.
15//!
16//! ## Features
17//!
18//! - **Message parsing**: Parse `.msg` files with support for primitive types, arrays, and constants
19//! - **Service parsing**: Parse `.srv` files with request/response separation
20//! - **Action parsing**: Parse `.action` files with goal/result/feedback sections
21//! - **Comprehensive validation**: Validates names, types, and values according to ROS2 standards
22//! - **Error handling**: Detailed error messages for debugging parsing issues
23//! - **Serde support**: Optional serialization support with the `serde` feature
24//!
25//! ## Quick Start
26//!
27//! ```rust
28//! use ros2msg::{parse_message_string, parse_service_string, parse_action_string};
29//!
30//! // Parse a message
31//! let msg_content = r#"
32//! int32 x
33//! int32 y
34//! string name
35//! "#;
36//! let msg_spec = parse_message_string("geometry_msgs", "Point", msg_content)?;
37//! println!("Parsed message: {}", msg_spec);
38//!
39//! // Parse a service
40//! let srv_content = r#"
41//! int32 a
42//! int32 b
43//! ---
44//! int32 sum
45//! "#;
46//! let srv_spec = parse_service_string("example_msgs", "AddTwoInts", srv_content)?;
47//! println!("Parsed service: {}", srv_spec);
48//!
49//! // Parse an action
50//! let action_content = r#"
51//! int32 order
52//! ---
53//! int32[] sequence
54//! ---
55//! int32[] partial_sequence
56//! "#;
57//! let action_spec = parse_action_string("example_msgs", "Fibonacci", action_content)?;
58//! println!("Parsed action: {}", action_spec);
59//! # Ok::<(), ros2msg::ParseError>(())
60//! ```
61//!
62//! ## Modules
63//!
64//! - [`msg`]: ROS2 message/service/action parser (.msg, .srv, .action files)
65//! - [`idl`]: ROS2 IDL parser (full IDL specification support)
66//! - [`generator`]: Code generator for converting ROS2 interfaces to Rust types
67//! - [`ros2args`]: ROS2 command-line arguments parser
68
69// Public modules
70/// ROS2 Message/Service/Action parser
71///
72/// This module handles the traditional ROS2 message format parsing for
73/// `.msg`, `.srv`, and `.action` files.
74pub mod msg;
75
76/// ROS2 IDL parser
77///
78/// This module provides full ROS2 IDL specification support for parsing
79/// advanced IDL files with complex types, modules, and annotations.
80pub mod idl;
81
82/// Code generator for ROS2 interfaces
83///
84/// This module provides a bindgen-style API for generating Rust code from
85/// ROS2 message, service, action, and IDL files.
86pub mod generator;
87
88/// MSG/SRV/Action to IDL converter
89///
90/// This module converts ROS2 message, service, and action definitions to IDL format,
91/// matching the behavior of rosidl_adapter.
92pub mod idl_adapter;
93
94// Re-export commonly used types and functions from the msg module
95// for backward compatibility
96pub use msg::{
97 ActionSpecification, AnnotationValue, Annotations, BaseType, Constant, Field,
98 InterfaceSpecification, MessageSpecification, PRIMITIVE_TYPES, ParseError, ParseResult,
99 PrimitiveValue, ServiceSpecification, Type, Value, create_feedback_message,
100 create_service_event_message, is_valid_constant_name, is_valid_field_name,
101 is_valid_message_name, is_valid_package_name, parse_action_file, parse_action_string,
102 parse_interface_file, parse_message_file, parse_message_string, parse_primitive_value_string,
103 parse_service_file, parse_service_string,
104};
105
106/// Version information
107pub const VERSION: &str = env!("CARGO_PKG_VERSION");
108
109/// Parse any ROS2 interface file based on its extension
110///
111/// Automatically detects the file type based on the extension:
112/// - `.msg` files are parsed as messages
113/// - `.srv` files are parsed as services
114/// - `.action` files are parsed as actions
115///
116/// # Arguments
117///
118/// * `pkg_name` - The package name containing the interface
119/// * `file_path` - Path to the interface file
120///
121/// # Returns
122///
123/// Returns an `InterfaceSpecification` enum containing the parsed specification
124///
125/// # Example
126///
127/// ```rust,no_run
128/// use ros2msg::{parse_interface_file, InterfaceSpecification};
129/// use std::path::Path;
130///
131/// let spec = parse_interface_file("geometry_msgs", Path::new("Point.msg"))?;
132/// match spec {
133/// InterfaceSpecification::Message(msg_spec) => {
134/// println!("Parsed message: {}", msg_spec.msg_name);
135/// }
136/// InterfaceSpecification::Service(srv_spec) => {
137/// println!("Parsed service: {}", srv_spec.srv_name);
138/// }
139/// InterfaceSpecification::Action(action_spec) => {
140/// println!("Parsed action: {}", action_spec.action_name);
141/// }
142/// }
143/// # Ok::<(), ros2msg::ParseError>(())
144/// ```
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn test_version() {
151 assert!(VERSION.chars().any(|c| c.is_ascii_digit()));
152 }
153
154 #[test]
155 fn test_interface_specification() {
156 let msg_spec = parse_message_string("test_msgs", "TestMsg", "int32 x").unwrap();
157 let interface_spec = InterfaceSpecification::Message(msg_spec);
158
159 assert!(matches!(interface_spec, InterfaceSpecification::Message(_)));
160 assert!(!matches!(
161 interface_spec,
162 InterfaceSpecification::Service(_)
163 ));
164 assert!(!matches!(interface_spec, InterfaceSpecification::Action(_)));
165
166 if let InterfaceSpecification::Message(msg) = &interface_spec {
167 assert_eq!(msg.pkg_name, "test_msgs");
168 assert_eq!(msg.msg_name, "TestMsg");
169 }
170 }
171
172 #[test]
173 fn test_comprehensive_parsing() {
174 // Test message parsing
175 let msg_content = r#"
176 # A test message
177 int32 x # X coordinate
178 int32 y # Y coordinate
179 string name "default"
180 "#;
181 let msg_spec = parse_message_string("geometry_msgs", "Point", msg_content).unwrap();
182 assert_eq!(msg_spec.fields.len(), 3);
183
184 // Test service parsing
185 let srv_content = r"
186 int32 a
187 int32 b
188 ---
189 int32 sum
190 ";
191 let srv_spec = parse_service_string("example_msgs", "AddTwoInts", srv_content).unwrap();
192 assert_eq!(srv_spec.request.fields.len(), 2);
193 assert_eq!(srv_spec.response.fields.len(), 1);
194
195 // Test action parsing
196 let action_content = r"
197 int32 order
198 ---
199 int32[] sequence
200 ---
201 int32[] partial_sequence
202 ";
203 let action_spec = parse_action_string("example_msgs", "Fibonacci", action_content).unwrap();
204 assert_eq!(action_spec.goal.fields.len(), 1);
205 assert_eq!(action_spec.result.fields.len(), 1);
206 assert_eq!(action_spec.feedback.fields.len(), 1);
207 }
208}