Skip to main content

nros_core/
message_info.rs

1//! Message metadata for received messages
2//!
3//! This module provides the `MessageInfo` type which contains metadata
4//! about received messages, matching the rclrs pattern.
5//!
6//! # Example
7//!
8//! ```text
9//! node.create_subscription("/topic", |msg: &Int32, info: &MessageInfo| {
10//!     println!("Received at {:?} from {:?}", info.source_timestamp(), info.publisher_gid());
11//! });
12//! ```
13
14use crate::Time;
15
16/// Size of the publisher Global Identifier (GID)
17pub const PUBLISHER_GID_SIZE: usize = 16;
18
19/// Metadata about a received message
20///
21/// Contains information about the source and timing of a message.
22/// This matches the rclrs `MessageInfo` type.
23#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
24pub struct MessageInfo {
25    /// Timestamp when the message was published (from the publisher's clock)
26    source_timestamp: Time,
27    /// Timestamp when the message was received (from the subscriber's clock)
28    received_timestamp: Time,
29    /// Sequence number of the message from the publisher
30    publication_sequence_number: i64,
31    /// Sequence number of the message at the subscriber
32    reception_sequence_number: i64,
33    /// Global identifier of the publisher
34    publisher_gid: [u8; PUBLISHER_GID_SIZE],
35}
36
37impl MessageInfo {
38    /// Create a new MessageInfo with all fields set to defaults
39    pub const fn new() -> Self {
40        Self {
41            source_timestamp: Time::new(0, 0),
42            received_timestamp: Time::new(0, 0),
43            publication_sequence_number: 0,
44            reception_sequence_number: 0,
45            publisher_gid: [0u8; PUBLISHER_GID_SIZE],
46        }
47    }
48
49    /// Create a MessageInfo with the given timestamps
50    pub const fn with_timestamps(source: Time, received: Time) -> Self {
51        Self {
52            source_timestamp: source,
53            received_timestamp: received,
54            publication_sequence_number: 0,
55            reception_sequence_number: 0,
56            publisher_gid: [0u8; PUBLISHER_GID_SIZE],
57        }
58    }
59
60    /// Get the timestamp when the message was published
61    pub const fn source_timestamp(&self) -> Time {
62        self.source_timestamp
63    }
64
65    /// Get the timestamp when the message was received
66    pub const fn received_timestamp(&self) -> Time {
67        self.received_timestamp
68    }
69
70    /// Get the publication sequence number
71    pub const fn publication_sequence_number(&self) -> i64 {
72        self.publication_sequence_number
73    }
74
75    /// Get the reception sequence number
76    pub const fn reception_sequence_number(&self) -> i64 {
77        self.reception_sequence_number
78    }
79
80    /// Get the publisher's Global Identifier (GID)
81    pub const fn publisher_gid(&self) -> &[u8; PUBLISHER_GID_SIZE] {
82        &self.publisher_gid
83    }
84
85    /// Set the source timestamp
86    pub fn set_source_timestamp(&mut self, ts: Time) {
87        self.source_timestamp = ts;
88    }
89
90    /// Set the received timestamp
91    pub fn set_received_timestamp(&mut self, ts: Time) {
92        self.received_timestamp = ts;
93    }
94
95    /// Set the publication sequence number
96    pub fn set_publication_sequence_number(&mut self, seq: i64) {
97        self.publication_sequence_number = seq;
98    }
99
100    /// Set the reception sequence number
101    pub fn set_reception_sequence_number(&mut self, seq: i64) {
102        self.reception_sequence_number = seq;
103    }
104
105    /// Set the publisher GID
106    pub fn set_publisher_gid(&mut self, gid: [u8; PUBLISHER_GID_SIZE]) {
107        self.publisher_gid = gid;
108    }
109}
110
111/// Raw-subscription message info: [`MessageInfo`] metadata plus the
112/// sample's wire-level **attachment** bytes, borrowed for the callback
113/// scope.
114///
115/// Surfaced on the generic (type-erased) subscription path
116/// (`node.subscription(t).generic(..).message_info().build(cb)` — the
117/// `FnMut(&[u8], &RawMessageInfo)` callback). The attachment carries
118/// out-of-band tags such as the cross-RMW bridge's `bridge_origin`
119/// (read via [`attachment`](Self::attachment) for echo suppression).
120///
121/// The `'a` lifetime ties the borrowed attachment to the dispatch call;
122/// copy out what you need before the callback returns. Metadata
123/// accessors delegate to the inner [`MessageInfo`]; on backends without
124/// a combined raw+info+attachment take they read their defaults (the
125/// attachment is always populated).
126#[derive(Debug, Clone, Copy)]
127pub struct RawMessageInfo<'a> {
128    info: MessageInfo,
129    attachment: &'a [u8],
130}
131
132impl<'a> RawMessageInfo<'a> {
133    /// Build from an attachment slice (metadata defaulted).
134    pub const fn new(attachment: &'a [u8]) -> Self {
135        Self {
136            info: MessageInfo::new(),
137            attachment,
138        }
139    }
140
141    /// Build from explicit metadata + attachment.
142    pub const fn with_info(info: MessageInfo, attachment: &'a [u8]) -> Self {
143        Self { info, attachment }
144    }
145
146    /// The sample's wire-level attachment bytes (empty if none).
147    pub const fn attachment(&self) -> &'a [u8] {
148        self.attachment
149    }
150
151    /// The underlying metadata.
152    pub const fn info(&self) -> &MessageInfo {
153        &self.info
154    }
155
156    /// Timestamp when the message was published.
157    pub const fn source_timestamp(&self) -> Time {
158        self.info.source_timestamp()
159    }
160
161    /// Timestamp when the message was received.
162    pub const fn received_timestamp(&self) -> Time {
163        self.info.received_timestamp()
164    }
165
166    /// Publisher's Global Identifier (GID).
167    pub const fn publisher_gid(&self) -> &[u8; PUBLISHER_GID_SIZE] {
168        self.info.publisher_gid()
169    }
170}
171
172#[cfg(test)]
173mod tests {
174    use super::*;
175
176    #[test]
177    fn test_message_info_default() {
178        let info = MessageInfo::new();
179        assert_eq!(info.source_timestamp(), Time::new(0, 0));
180        assert_eq!(info.publication_sequence_number(), 0);
181        assert_eq!(info.publisher_gid(), &[0u8; PUBLISHER_GID_SIZE]);
182    }
183
184    #[test]
185    fn test_message_info_with_timestamps() {
186        let source = Time::new(1, 500_000_000);
187        let received = Time::new(1, 600_000_000);
188        let info = MessageInfo::with_timestamps(source, received);
189        assert_eq!(info.source_timestamp(), source);
190        assert_eq!(info.received_timestamp(), received);
191    }
192}