use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Connection {
pub id: u32,
pub topic: String,
pub message_type: String,
pub message_definition: MessageDefinition,
pub type_description_hash: String,
pub message_count: u64,
pub serialization_format: String,
pub offered_qos_profiles: Vec<QosProfile>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MessageDefinition {
pub format: MessageDefinitionFormat,
pub data: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MessageDefinitionFormat {
None,
Msg,
Idl,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct QosProfile {
pub history: QosHistory,
pub depth: u32,
pub reliability: QosReliability,
pub durability: QosDurability,
pub deadline: QosTime,
pub lifespan: QosTime,
pub liveliness: QosLiveliness,
pub liveliness_lease_duration: QosTime,
pub avoid_ros_namespace_conventions: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum QosHistory {
SystemDefault,
KeepLast,
KeepAll,
Unknown,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum QosReliability {
SystemDefault,
Reliable,
BestEffort,
Unknown,
BestAvailable,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum QosDurability {
SystemDefault,
TransientLocal,
Volatile,
Unknown,
BestAvailable,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum QosLiveliness {
SystemDefault,
Automatic,
ManualByNode,
ManualByTopic,
Unknown,
BestAvailable,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct QosTime {
pub sec: i32,
pub nsec: u32,
}
#[derive(Debug, Clone)]
pub struct TopicInfo {
pub name: String,
pub message_type: String,
pub message_definition: MessageDefinition,
pub message_count: u64,
pub connections: Vec<Connection>,
}
#[derive(Debug, Clone)]
pub struct Message {
pub connection: Connection,
pub topic: String,
pub timestamp: u64,
pub data: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct RawMessage {
pub connection: Connection,
pub timestamp: u64,
pub raw_data: Vec<u8>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Duration {
pub nanoseconds: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct StartingTime {
pub nanoseconds_since_epoch: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompressionMode {
None,
Message,
File,
Storage,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompressionFormat {
None,
Zstd,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StoragePlugin {
Sqlite3,
Mcap,
}
impl Default for MessageDefinition {
fn default() -> Self {
Self {
format: MessageDefinitionFormat::None,
data: String::new(),
}
}
}
impl Default for QosProfile {
fn default() -> Self {
Self {
history: QosHistory::SystemDefault,
depth: 0,
reliability: QosReliability::SystemDefault,
durability: QosDurability::SystemDefault,
deadline: QosTime::default(),
lifespan: QosTime::default(),
liveliness: QosLiveliness::SystemDefault,
liveliness_lease_duration: QosTime::default(),
avoid_ros_namespace_conventions: false,
}
}
}
impl Connection {
pub fn msgtype(&self) -> &str {
&self.message_type
}
pub fn msgcount(&self) -> u64 {
self.message_count
}
}
impl CompressionMode {
pub fn as_str(&self) -> &'static str {
match self {
CompressionMode::None => "",
CompressionMode::Message => "message",
CompressionMode::File => "file",
CompressionMode::Storage => "storage",
}
}
}
impl CompressionFormat {
pub fn as_str(&self) -> &'static str {
match self {
CompressionFormat::None => "",
CompressionFormat::Zstd => "zstd",
}
}
}
impl StoragePlugin {
pub fn as_str(&self) -> &'static str {
match self {
StoragePlugin::Sqlite3 => "sqlite3",
StoragePlugin::Mcap => "mcap",
}
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn connection_msgtype_alias() {
let conn = Connection {
id: 1,
topic: "/imu".to_string(),
message_type: "sensor_msgs/msg/Imu".to_string(),
message_definition: MessageDefinition::default(),
type_description_hash: String::new(),
message_count: 42,
serialization_format: "cdr".to_string(),
offered_qos_profiles: Vec::new(),
};
assert_eq!(conn.msgtype(), "sensor_msgs/msg/Imu");
assert_eq!(conn.msgcount(), 42);
}
#[test]
fn compression_mode_as_str() {
assert_eq!(CompressionMode::None.as_str(), "");
assert_eq!(CompressionMode::Message.as_str(), "message");
assert_eq!(CompressionMode::File.as_str(), "file");
assert_eq!(CompressionMode::Storage.as_str(), "storage");
}
#[test]
fn compression_format_as_str() {
assert_eq!(CompressionFormat::None.as_str(), "");
assert_eq!(CompressionFormat::Zstd.as_str(), "zstd");
}
#[test]
fn storage_plugin_as_str() {
assert_eq!(StoragePlugin::Sqlite3.as_str(), "sqlite3");
assert_eq!(StoragePlugin::Mcap.as_str(), "mcap");
}
#[test]
fn qos_profile_default_uses_system_default() {
let qos = QosProfile::default();
assert!(matches!(qos.history, QosHistory::SystemDefault));
assert!(matches!(qos.reliability, QosReliability::SystemDefault));
assert!(matches!(qos.durability, QosDurability::SystemDefault));
assert!(matches!(qos.liveliness, QosLiveliness::SystemDefault));
assert_eq!(qos.depth, 0);
assert!(!qos.avoid_ros_namespace_conventions);
}
#[test]
fn message_definition_default_is_none_format() {
let def = MessageDefinition::default();
assert!(matches!(def.format, MessageDefinitionFormat::None));
assert!(def.data.is_empty());
}
#[test]
fn qos_time_default_is_zero() {
let t = QosTime::default();
assert_eq!(t.sec, 0);
assert_eq!(t.nsec, 0);
}
}