Skip to main content

ros2_types/
traits.rs

1//! Core traits for ROS2 message types
2//!
3//! This module provides traits for ROS2 messages, services, and actions
4//! that are used by the derive macros.
5
6use crate::Result;
7use std::ffi::c_void;
8
9/// Trait for types that have type support information.
10///
11/// This allows the runtime to understand the structure of messages
12/// for serialization and deserialization.
13///
14/// # Serialization
15///
16/// The `to_bytes` and `from_bytes` methods provide CDR serialization:
17/// - For RCL (DDS-based): Uses `rmw_serialize`/`rmw_deserialize` internally
18/// - For native Zenoh: Uses serde with `cdr-encoding` crate
19pub trait TypeSupport: 'static + Send + Sync {
20    /// Returns an opaque pointer to the type support structure.
21    ///
22    /// The actual type of this pointer depends on the implementation
23    /// (e.g., `rosidl_message_type_support_t` in RCL).
24    fn type_support() -> *const c_void {
25        std::ptr::null()
26    }
27
28    /// Serialize this message to CDR-encoded bytes.
29    ///
30    /// # Implementation
31    /// - `rcl` feature: Uses RMW serialization functions
32    /// - `zenoh` feature: Uses serde + cdr-encoding crate
33    ///
34    /// # Errors
35    /// Returns `Error::CdrError` if serialization fails.
36    fn to_bytes(&self) -> Result<Vec<u8>>;
37
38    /// Deserialize a message from CDR-encoded bytes.
39    ///
40    /// # Implementation
41    /// - `rcl` feature: Uses RMW deserialization functions
42    /// - `zenoh` feature: Uses serde + cdr-encoding crate
43    ///
44    /// # Errors
45    /// Returns `Error::CdrError` if deserialization fails.
46    fn from_bytes(bytes: &[u8]) -> Result<Self>
47    where
48        Self: Sized;
49
50    /// Returns the type name in DDS format.
51    ///
52    /// Example: `"std_msgs::msg::dds_::String_"`
53    ///
54    /// This is used for Zenoh key expressions and type matching.
55    fn type_name() -> &'static str;
56
57    /// Returns the RIHS01 type hash for this message type.
58    ///
59    /// # Implementation
60    /// - For RCL: Returns empty string (hash is handled by rosidl typesupport)
61    /// - For Zenoh: Computes hash from TypeDescription
62    ///
63    /// The hash format is: `RIHS01_<64_character_hex_sha256>`
64    fn type_hash() -> Result<::std::string::String> {
65        Ok("RIHS01_00".to_string())
66    }
67}
68
69/// Trait for type that can fail cloning
70///
71/// Used for FFI types where cloning may fail due to memory allocation.
72pub trait TryClone: Sized {
73    /// Returns Some(Self) if clone succeeds else None
74    fn try_clone(&self) -> Option<Self>;
75}
76
77/// Trait for ROS2 service message types.
78///
79/// Services consist of a request and response message pair.
80pub trait ServiceMsg: 'static + Send + Sync {
81    /// The request message type.
82    type Request: TypeSupport;
83
84    /// The response message type.
85    type Response: TypeSupport;
86
87    /// Returns an opaque pointer to the service type support structure.
88    fn type_support() -> *const c_void {
89        std::ptr::null()
90    }
91
92    /// Returns the type name in DDS format.
93    ///
94    /// Example: `"example_interfaces::srv::dds_::AddTwoInts_"`
95    ///
96    /// This is used for Zenoh key expressions and type matching.
97    fn type_name() -> &'static str;
98
99    /// Returns the RIHS01 type hash for this message type.
100    ///
101    /// # Implementation
102    /// - For RCL: Returns empty string (hash is handled by rosidl typesupport)
103    /// - For Zenoh: Computes hash from TypeDescription
104    ///
105    /// The hash format is: `RIHS01_<64_character_hex_sha256>`
106    fn type_hash() -> Result<::std::string::String> {
107        Ok("RIHS01_00".to_string())
108    }
109}
110
111/// Trait for ROS2 action message types.
112///
113/// Actions are more complex than services and include goals, results,
114/// and feedback messages.
115pub trait ActionMsg: 'static + Send + Sync {
116    /// The goal service type.
117    type Goal: ActionGoal;
118
119    /// The result service type.
120    type Result: ActionResult;
121
122    /// The feedback message type.
123    type Feedback: TypeSupport + GetUUID;
124
125    /// Returns an opaque pointer to the action type support structure.
126    fn type_support() -> *const c_void {
127        std::ptr::null()
128    }
129
130    /// Returns the type name in DDS format.
131    ///
132    /// Example: `"example_interfaces::srv::dds_::AddTwoInts_"`
133    ///
134    /// This is used for Zenoh key expressions and type matching.
135    fn type_name() -> &'static str;
136
137    /// The goal content type (the actual goal data).
138    type GoalContent: TypeSupport;
139
140    /// Create a new goal request with the given goal and UUID.
141    fn new_goal_request(
142        goal: Self::GoalContent,
143        uuid: [u8; 16],
144    ) -> <Self::Goal as ActionGoal>::Request;
145
146    /// The result content type (the actual result data).
147    type ResultContent: TypeSupport + TryClone;
148
149    /// Create a new result response with the given status and result.
150    fn new_result_response(
151        status: u8,
152        result: Self::ResultContent,
153    ) -> <Self::Result as ActionResult>::Response;
154
155    /// The feedback content type (the actual feedback data).
156    type FeedbackContent: TypeSupport;
157
158    /// Create a new feedback message with the given feedback and UUID.
159    fn new_feedback_message(feedback: Self::FeedbackContent, uuid: [u8; 16]) -> Self::Feedback;
160
161    /// Returns the RIHS01 type hash for this message type.
162    ///
163    /// # Implementation
164    /// - For RCL: Returns empty string (hash is handled by rosidl typesupport)
165    /// - For Zenoh: Computes hash from TypeDescription
166    ///
167    /// The hash format is: `RIHS01_<64_character_hex_sha256>`
168    fn type_hash() -> Result<::std::string::String> {
169        Ok("RIHS01_00".to_string())
170    }
171}
172
173/// Trait for action goal types.
174pub trait ActionGoal: 'static + Send + Sync {
175    /// The request message type for sending a goal.
176    type Request: TypeSupport + GetUUID;
177
178    /// The response message type for goal acceptance/rejection.
179    type Response: TypeSupport + GoalResponse;
180
181    /// Returns an opaque pointer to the goal service type support structure.
182    fn type_support() -> *const c_void {
183        std::ptr::null()
184    }
185}
186
187/// Trait for types that contain a UUID.
188///
189/// Used for tracking goals and feedback in actions.
190pub trait GetUUID: 'static + Send + Sync {
191    /// Returns a reference to the UUID.
192    fn get_uuid(&self) -> &[u8; 16];
193}
194
195/// Trait for action goal response types.
196pub trait GoalResponse: 'static + Send + Sync {
197    /// Returns whether the goal was accepted.
198    fn is_accepted(&self) -> bool;
199
200    /// Returns the timestamp of the response.
201    fn get_time_stamp(&self) -> UnsafeTime;
202
203    /// Creates a new goal response with the given acceptance status and timestamp.
204    fn new(accepted: bool, stamp: UnsafeTime) -> Self;
205}
206
207/// Trait for action result types.
208pub trait ActionResult: 'static + Send + Sync {
209    /// The request message type for getting a result.
210    type Request: TypeSupport + GetUUID;
211
212    /// The response message type containing the result.
213    type Response: TypeSupport + ResultResponse;
214
215    /// Returns an opaque pointer to the result service type support structure.
216    fn type_support() -> *const c_void {
217        std::ptr::null()
218    }
219}
220
221/// Trait for action result response types.
222pub trait ResultResponse: 'static + Send + Sync {
223    /// Returns the status code of the result.
224    fn get_status(&self) -> u8;
225}
226
227/// Represents a timestamp that may not be safe across all platforms.
228///
229/// The "Unsafe" prefix indicates this is subject to the year-2038 problem
230/// on 32-bit systems since `sec` is an `i32`.
231///
232/// This is compatible with ROS2's builtin_interfaces/Time.
233#[repr(C)]
234#[derive(
235    Debug,
236    Clone,
237    Copy,
238    Default,
239    PartialEq,
240    Eq,
241    PartialOrd,
242    Ord,
243    serde::Serialize,
244    serde::Deserialize,
245)]
246pub struct UnsafeTime {
247    /// Seconds since UNIX epoch.
248    pub sec: i32,
249    /// Nanoseconds component (0-999999999).
250    pub nanosec: u32,
251}
252
253impl UnsafeTime {
254    /// Creates a new UnsafeTime instance.
255    pub const fn new(sec: i32, nanosec: u32) -> Self {
256        Self { sec, nanosec }
257    }
258
259    /// Creates an UnsafeTime representing the UNIX epoch (0 seconds).
260    pub const fn zero() -> Self {
261        Self { sec: 0, nanosec: 0 }
262    }
263}
264
265// Conversions to/from std types for UnsafeTime
266use std::time::{Duration, SystemTime};
267
268impl From<&SystemTime> for UnsafeTime {
269    fn from(t: &SystemTime) -> Self {
270        let dur = t.duration_since(SystemTime::UNIX_EPOCH).unwrap();
271
272        let sec = dur.as_secs();
273        if sec > i32::MAX as u64 {
274            panic!("SystemTime too far in future (year-2038 problem)");
275        }
276
277        UnsafeTime {
278            sec: sec as i32,
279            nanosec: dur.subsec_nanos(),
280        }
281    }
282}
283
284impl From<SystemTime> for UnsafeTime {
285    fn from(t: SystemTime) -> Self {
286        (&t).into()
287    }
288}
289
290impl From<&UnsafeTime> for SystemTime {
291    fn from(t: &UnsafeTime) -> Self {
292        let nanos = Duration::from_nanos(t.nanosec as u64);
293        let secs = Duration::from_secs(t.sec as u64);
294        let dur = nanos + secs;
295        SystemTime::UNIX_EPOCH + dur
296    }
297}
298
299impl From<UnsafeTime> for SystemTime {
300    fn from(t: UnsafeTime) -> Self {
301        (&t).into()
302    }
303}
304
305/// Represents a duration that may not be safe across all platforms.
306///
307/// The "Unsafe" prefix indicates this is subject to the year-2038 problem
308/// on 32-bit systems since `sec` is an `i32`.
309#[repr(C)]
310#[derive(
311    Debug,
312    Clone,
313    Copy,
314    Default,
315    PartialEq,
316    Eq,
317    PartialOrd,
318    Ord,
319    serde::Serialize,
320    serde::Deserialize,
321)]
322pub struct UnsafeDuration {
323    /// Seconds component.
324    pub sec: i32,
325    /// Nanoseconds component.
326    pub nanosec: u32,
327}
328
329impl UnsafeDuration {
330    /// Creates a new UnsafeDuration instance.
331    pub const fn new(sec: i32, nanosec: u32) -> Self {
332        Self { sec, nanosec }
333    }
334
335    /// Creates a zero duration.
336    pub const fn zero() -> Self {
337        Self { sec: 0, nanosec: 0 }
338    }
339}
340
341impl From<&Duration> for UnsafeDuration {
342    fn from(t: &Duration) -> Self {
343        let sec = t.as_secs();
344
345        if sec > i32::MAX as u64 {
346            panic!("Duration too long (year-2038 problem)");
347        }
348
349        let nanosec = t.subsec_nanos();
350
351        UnsafeDuration {
352            sec: sec as i32,
353            nanosec,
354        }
355    }
356}
357
358impl From<Duration> for UnsafeDuration {
359    fn from(t: Duration) -> Self {
360        (&t).into()
361    }
362}
363
364impl From<&UnsafeDuration> for Duration {
365    fn from(t: &UnsafeDuration) -> Self {
366        Duration::from_secs(t.sec as u64) + Duration::from_nanos(t.nanosec as u64)
367    }
368}
369
370impl From<UnsafeDuration> for Duration {
371    fn from(t: UnsafeDuration) -> Self {
372        (&t).into()
373    }
374}
375
376/// Raw sequence type for FFI compatibility
377///
378/// This represents a dynamically-sized sequence of elements as used in ROS2 C API.
379///
380/// # Memory Management
381///
382/// - **With `rcl` feature**: Memory is managed by ROS2 C libraries through FFI.
383///   Use `TryClone` for proper copying via FFI functions.
384/// - **Without `rcl` feature**: Memory can be managed by Rust using `from_vec()`.
385///   The sequence takes ownership and will free memory on drop.
386#[repr(C)]
387#[derive(Debug)]
388pub struct SequenceRaw<T> {
389    /// Pointer to the data array
390    pub data: *mut T,
391    /// Current number of elements
392    pub size: usize,
393    /// Allocated capacity
394    pub capacity: usize,
395}
396
397impl<T> SequenceRaw<T> {
398    /// Create a null/empty sequence
399    pub const fn null() -> Self {
400        Self {
401            data: std::ptr::null_mut(),
402            size: 0,
403            capacity: 0,
404        }
405    }
406
407    /// Check if the sequence is empty
408    pub fn is_empty(&self) -> bool {
409        self.size == 0
410    }
411
412    /// Get the length of the sequence
413    pub fn len(&self) -> usize {
414        self.size
415    }
416
417    /// Get a slice of the sequence data
418    ///
419    pub fn as_slice(&self) -> &[T] {
420        if self.data.is_null() || self.size == 0 {
421            &[]
422        } else {
423            unsafe { std::slice::from_raw_parts(self.data, self.size) }
424        }
425    }
426    pub fn iter(&self) -> std::slice::Iter<'_, T> {
427        self.as_slice().iter()
428    }
429
430    /// Get a mutable slice of the sequence data
431    ///
432    #[deprecated(note = "use as_mut_slice instead")]
433    pub fn as_slice_mut(&mut self) -> &mut [T] {
434        if self.data.is_null() || self.size == 0 {
435            &mut []
436        } else {
437            unsafe { std::slice::from_raw_parts_mut(self.data, self.size) }
438        }
439    }
440
441    /// Get a mutable slice of the sequence data
442    ///
443    pub fn as_mut_slice(&mut self) -> &mut [T] {
444        if self.data.is_null() || self.size == 0 {
445            &mut []
446        } else {
447            unsafe { std::slice::from_raw_parts_mut(self.data, self.size) }
448        }
449    }
450    pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
451        self.as_mut_slice().iter_mut()
452    }
453}
454
455// Non-rcl implementations: Rust-managed memory using Vec
456#[cfg(not(feature = "rcl"))]
457impl<T> SequenceRaw<T> {
458    /// Create a sequence from a Vec (takes ownership)
459    ///
460    /// The Vec's memory is transferred to the sequence. The sequence will
461    /// free the memory when dropped.
462    pub fn from_vec(mut vec: Vec<T>) -> Self {
463        let data = vec.as_mut_ptr();
464        let size = vec.len();
465        let capacity = vec.capacity();
466        std::mem::forget(vec); // Don't drop the Vec, we own the memory now
467        Self {
468            data,
469            size,
470            capacity,
471        }
472    }
473
474    /// Convert the sequence back to a Vec (takes ownership)
475    ///
476    /// # Safety
477    /// Only call this on sequences created with `from_vec()` or that you know
478    /// were allocated by Rust.
479    pub unsafe fn into_vec(self) -> Vec<T> {
480        if self.data.is_null() {
481            Vec::new()
482        } else {
483            let vec = unsafe { Vec::from_raw_parts(self.data, self.size, self.capacity) };
484            std::mem::forget(self); // Don't run our Drop
485            vec
486        }
487    }
488}
489
490// Clone implementation for non-rcl: actually clone the data
491#[cfg(not(feature = "rcl"))]
492impl<T: Clone> Clone for SequenceRaw<T> {
493    fn clone(&self) -> Self {
494        if self.data.is_null() || self.size == 0 {
495            Self::null()
496        } else {
497            // Clone the data into a new Vec
498            let slice = unsafe { std::slice::from_raw_parts(self.data, self.size) };
499            let vec: Vec<T> = slice.to_vec();
500            Self::from_vec(vec)
501        }
502    }
503}
504
505// Clone implementation for rcl: shallow copy (use TryClone for proper cloning)
506#[cfg(feature = "rcl")]
507impl<T> Clone for SequenceRaw<T> {
508    fn clone(&self) -> Self {
509        // For rcl, we do a shallow copy. The actual data cloning should be done
510        // through TryClone which uses FFI copy functions.
511        Self {
512            data: self.data,
513            size: self.size,
514            capacity: self.capacity,
515        }
516    }
517}
518
519// PartialEq implementation: compare actual elements
520impl<T: PartialEq> PartialEq for SequenceRaw<T> {
521    fn eq(&self, other: &Self) -> bool {
522        if self.size != other.size {
523            return false;
524        }
525        if self.data.is_null() && other.data.is_null() {
526            return true;
527        }
528        if self.data.is_null() || other.data.is_null() {
529            return false;
530        }
531        // Compare actual elements
532        let self_slice = unsafe { std::slice::from_raw_parts(self.data, self.size) };
533        let other_slice = unsafe { std::slice::from_raw_parts(other.data, other.size) };
534        self_slice == other_slice
535    }
536}
537
538// Default implementation for SequenceRaw
539impl<T> Default for SequenceRaw<T> {
540    fn default() -> Self {
541        Self::null()
542    }
543}
544
545// Drop implementation for non-rcl: free Rust-managed memory
546#[cfg(not(feature = "rcl"))]
547impl<T> Drop for SequenceRaw<T> {
548    fn drop(&mut self) {
549        if !self.data.is_null() && self.capacity > 0 {
550            // Reconstruct the Vec and let it drop, freeing the memory
551            unsafe {
552                let _ = Vec::from_raw_parts(self.data, self.size, self.capacity);
553            }
554        }
555    }
556}
557
558// Serde implementations for non-rcl: serialize as a sequence
559#[cfg(not(feature = "rcl"))]
560impl<T: serde::Serialize> serde::Serialize for SequenceRaw<T> {
561    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
562    where
563        S: serde::Serializer,
564    {
565        use serde::ser::SerializeSeq;
566        let slice = self.as_slice();
567        let mut seq = serializer.serialize_seq(Some(slice.len()))?;
568        for element in slice {
569            seq.serialize_element(element)?;
570        }
571        seq.end()
572    }
573}
574
575#[cfg(not(feature = "rcl"))]
576impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for SequenceRaw<T> {
577    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
578    where
579        D: serde::Deserializer<'de>,
580    {
581        let vec = Vec::<T>::deserialize(deserializer)?;
582        Ok(Self::from_vec(vec))
583    }
584}
585
586unsafe impl<T: Send> Send for SequenceRaw<T> {}
587unsafe impl<T: Sync> Sync for SequenceRaw<T> {}