Skip to main content

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}