diff --git a/aimdb-zenoh-connector/src/profile/attachment.rs b/aimdb-zenoh-connector/src/profile/attachment.rs index 8642a708..41eeb454 100644 --- a/aimdb-zenoh-connector/src/profile/attachment.rs +++ b/aimdb-zenoh-connector/src/profile/attachment.rs @@ -2,10 +2,12 @@ /// Its length: sequence number, source timestamp, the `0x10` length of the /// GID array, and the GID. +#[cfg_attr(not(feature = "std"), allow(dead_code))] pub(crate) const ATTACHMENT_LEN: usize = 8 + 8 + 1 + 16; /// A publication's attachment, as zenoh-cpp's `ext::Serializer` writes it. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(not(feature = "std"), allow(dead_code))] pub(crate) struct Attachment { /// Per publisher, starting at 1. pub sequence: i64, @@ -15,6 +17,7 @@ pub(crate) struct Attachment { } impl Attachment { + #[cfg_attr(not(feature = "std"), allow(dead_code))] pub(crate) fn encode(&self) -> [u8; ATTACHMENT_LEN] { let mut out = [0; ATTACHMENT_LEN]; out[..8].copy_from_slice(&self.sequence.to_le_bytes()); @@ -25,6 +28,7 @@ impl Attachment { } /// `None` unless `bytes` has exactly this layout. + #[cfg(test)] pub(crate) fn decode(bytes: &[u8]) -> Option { let bytes: &[u8; ATTACHMENT_LEN] = bytes.try_into().ok()?; if bytes[16] != 16 { diff --git a/aimdb-zenoh-connector/src/profile/gid.rs b/aimdb-zenoh-connector/src/profile/gid.rs index 2e5ef356..9626814d 100644 --- a/aimdb-zenoh-connector/src/profile/gid.rs +++ b/aimdb-zenoh-connector/src/profile/gid.rs @@ -2,6 +2,7 @@ /// The GID rmw_zenoh derives from an entity's liveliness token: XXH3-128 of /// the token, the low 64 bits then the high 64 bits, each little-endian. +#[cfg_attr(not(feature = "std"), allow(dead_code))] pub(crate) fn gid(token: &str) -> [u8; 16] { let hash = twox_hash::XxHash3_128::oneshot(token.as_bytes()); let mut out = [0; 16]; diff --git a/aimdb-zenoh-connector/src/profile/keys.rs b/aimdb-zenoh-connector/src/profile/keys.rs index 6acd1e7f..2f73f5dd 100644 --- a/aimdb-zenoh-connector/src/profile/keys.rs +++ b/aimdb-zenoh-connector/src/profile/keys.rs @@ -7,6 +7,7 @@ use super::Qos; /// A ROS 2 node's identity in liveliness tokens. #[derive(Debug, Clone, Copy)] +#[cfg_attr(not(feature = "std"), allow(dead_code))] pub(crate) struct Node<'a> { pub domain: u32, /// The Zenoh session id, as Zenoh prints it. @@ -21,6 +22,7 @@ pub(crate) struct Node<'a> { /// Whether an entity publishes or subscribes. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(not(feature = "std"), allow(dead_code))] pub(crate) enum EntityKind { Publisher, Subscription, @@ -28,10 +30,12 @@ pub(crate) enum EntityKind { /// A name as a single token chunk: every `/` becomes `%`, and an empty name /// is `%`. Written straight into the token, with no string of its own. +#[cfg_attr(not(feature = "std"), allow(dead_code))] pub(crate) fn mangle(name: &str) -> Mangled<'_> { Mangled(name) } +#[cfg_attr(not(feature = "std"), allow(dead_code))] pub(crate) struct Mangled<'a>(&'a str); impl fmt::Display for Mangled<'_> { @@ -51,12 +55,14 @@ impl fmt::Display for Mangled<'_> { /// `///`, the topic without its slashes at either /// end: `0/cell4/temperature/sensor_msgs::msg::dds_::Temperature_/RIHS01_…`. +#[cfg_attr(not(feature = "std"), allow(dead_code))] pub(crate) fn data_key(domain: u32, topic: &str, type_name: &str, type_hash: &str) -> String { let topic = topic.trim_matches('/'); alloc::format!("{domain}/{topic}/{type_name}/{type_hash}") } /// `@ros2_lv/////NN///`. +#[cfg_attr(not(feature = "std"), allow(dead_code))] pub(crate) fn node_token(node: &Node<'_>) -> String { alloc::format!( "@ros2_lv/{}/{}/{}/{}/NN/{}/{}/{}", @@ -71,6 +77,7 @@ pub(crate) fn node_token(node: &Node<'_>) -> String { } /// `@ros2_lv/////MP|MS///////`. +#[cfg_attr(not(feature = "std"), allow(dead_code))] pub(crate) fn entity_token( node: &Node<'_>, entity_id: u32, diff --git a/aimdb-zenoh-connector/src/profile/mod.rs b/aimdb-zenoh-connector/src/profile/mod.rs index baf43297..460e7543 100644 --- a/aimdb-zenoh-connector/src/profile/mod.rs +++ b/aimdb-zenoh-connector/src/profile/mod.rs @@ -4,21 +4,28 @@ //! Pure functions over strings and bytes, `no_std + alloc`, so a ROS node on //! an MCU can reuse them unchanged. -// Used by the ROS 2 connector, which builds on this module. -#![allow(dead_code, unused_imports)] - mod attachment; mod gid; mod keys; mod names; mod qos; -pub(crate) use attachment::{Attachment, ATTACHMENT_LEN}; +#[cfg(any(feature = "std", test))] +pub(crate) use attachment::Attachment; +#[cfg(test)] +pub(crate) use attachment::ATTACHMENT_LEN; +#[cfg(any(feature = "std", test))] pub(crate) use gid::gid; -pub(crate) use keys::{data_key, entity_token, mangle, node_token, EntityKind, Node}; -pub(crate) use names::{validate_namespace, validate_node_name, validate_topic, NameError}; +#[cfg(test)] +pub(crate) use keys::mangle; +#[cfg(any(feature = "std", test))] +pub(crate) use keys::{data_key, entity_token, node_token, EntityKind, Node}; +#[cfg(feature = "std")] +pub(crate) use names::{validate_namespace, validate_node_name, validate_topic}; +pub(crate) use qos::Qos; pub use qos::Reliability; -pub(crate) use qos::{Durability, History, Qos}; +#[cfg(any(feature = "std", test))] +pub(crate) use qos::{Durability, History}; #[cfg(test)] mod golden; diff --git a/aimdb-zenoh-connector/src/profile/names.rs b/aimdb-zenoh-connector/src/profile/names.rs index 86f5f1c9..f07ee214 100644 --- a/aimdb-zenoh-connector/src/profile/names.rs +++ b/aimdb-zenoh-connector/src/profile/names.rs @@ -3,14 +3,18 @@ use core::fmt; /// The longest fully qualified topic name (`RMW_TOPIC_MAX_NAME_LENGTH`). +#[cfg_attr(not(feature = "std"), allow(dead_code))] const TOPIC_MAX: usize = 255 - 8; /// The longest namespace (`RMW_NAMESPACE_MAX_LENGTH`). +#[cfg_attr(not(feature = "std"), allow(dead_code))] const NAMESPACE_MAX: usize = TOPIC_MAX - 2; /// The longest node name (`RMW_NODE_NAME_MAX_NAME_LENGTH`). +#[cfg_attr(not(feature = "std"), allow(dead_code))] const NODE_NAME_MAX: usize = 255; /// Why a topic, namespace or node name breaks the ROS rules. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(not(feature = "std"), allow(dead_code))] pub(crate) enum NameError { Empty, NotAbsolute, @@ -36,11 +40,13 @@ impl fmt::Display for NameError { } /// A fully qualified topic name: `/cell4/temperature`. +#[cfg_attr(not(feature = "std"), allow(dead_code))] pub(crate) fn validate_topic(name: &str) -> Result<(), NameError> { absolute(name, TOPIC_MAX) } /// A namespace: `/` or `/cell4`. +#[cfg_attr(not(feature = "std"), allow(dead_code))] pub(crate) fn validate_namespace(name: &str) -> Result<(), NameError> { if name == "/" { return Ok(()); @@ -49,6 +55,7 @@ pub(crate) fn validate_namespace(name: &str) -> Result<(), NameError> { } /// A node name: `cell4_gateway`. +#[cfg_attr(not(feature = "std"), allow(dead_code))] pub(crate) fn validate_node_name(name: &str) -> Result<(), NameError> { let Some(first) = name.bytes().next() else { return Err(NameError::Empty); @@ -65,6 +72,7 @@ pub(crate) fn validate_node_name(name: &str) -> Result<(), NameError> { Ok(()) } +#[cfg_attr(not(feature = "std"), allow(dead_code))] fn absolute(name: &str, max: usize) -> Result<(), NameError> { if name.is_empty() { return Err(NameError::Empty); diff --git a/aimdb-zenoh-connector/src/profile/qos.rs b/aimdb-zenoh-connector/src/profile/qos.rs index d8aae66e..02d80056 100644 --- a/aimdb-zenoh-connector/src/profile/qos.rs +++ b/aimdb-zenoh-connector/src/profile/qos.rs @@ -1,14 +1,17 @@ //! The QoS field of a liveliness token. +#[cfg(test)] use alloc::string::String; use core::fmt; /// rmw_zenoh's own default depth: a token leaves the depth empty for it. +#[cfg_attr(not(feature = "std"), allow(dead_code))] const RMW_ZENOH_DEFAULT_DEPTH: u32 = 42; /// The QoS a `ros2://` link advertises. Deadline, lifespan and liveliness /// always take the ROS defaults. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(not(feature = "std"), allow(dead_code))] pub(crate) struct Qos { pub reliability: Reliability, pub durability: Durability, @@ -27,6 +30,7 @@ pub enum Reliability { impl Reliability { /// The link config value. + #[cfg_attr(not(feature = "std"), allow(dead_code))] pub(crate) fn as_str(self) -> &'static str { match self { Self::Reliable => "reliable", @@ -35,6 +39,7 @@ impl Reliability { } /// The inverse of [`as_str`](Self::as_str). + #[cfg_attr(not(feature = "std"), allow(dead_code))] pub(crate) fn parse(value: &str) -> Option { match value { "reliable" => Some(Self::Reliable), @@ -45,14 +50,18 @@ impl Reliability { } #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(not(feature = "std"), allow(dead_code))] pub(crate) enum Durability { Volatile, + #[allow(dead_code)] TransientLocal, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(not(feature = "std"), allow(dead_code))] pub(crate) enum History { KeepLast, + #[allow(dead_code)] KeepAll, } @@ -71,6 +80,7 @@ impl Default for Qos { impl Qos { /// The token's QoS field; see the [`Display`](fmt::Display) impl. + #[cfg(test)] pub(crate) fn encode(&self) -> String { alloc::format!("{self}") } diff --git a/tools/aimdb-cli/src/error.rs b/tools/aimdb-cli/src/error.rs index 7fc67a82..2b7e45a2 100644 --- a/tools/aimdb-cli/src/error.rs +++ b/tools/aimdb-cli/src/error.rs @@ -20,7 +20,6 @@ pub const EXIT_TIMEOUT: i32 = 4; /// CLI-specific errors with helpful messages and hints #[derive(Debug, Error)] -#[allow(dead_code)] // Some variants are for future use pub enum CliError { /// Failed to connect to AimDB instance #[error("Connection failed: {endpoint}\n Reason: {reason}\n Hint: Check if AimDB instance is running")] @@ -35,14 +34,17 @@ pub enum CliError { NoInstancesFound, /// Requested record does not exist + #[allow(dead_code)] #[error("Record '{name}' not found\n Use 'aimdb record list' to see available records")] RecordNotFound { name: String }, /// Attempted write operation on read-only record + #[allow(dead_code)] #[error("Permission denied: {operation}\n Record '{name}' is not writable\n Hint: Check 'writable' column in 'aimdb record list'")] WriteProtected { operation: String, name: String }, /// Protocol version mismatch between client and server + #[allow(dead_code)] #[error("Protocol error: {message}\n Server version: {server_version}\n Client version: {client_version}")] ProtocolMismatch { message: String, @@ -138,7 +140,6 @@ impl CliError { } } -#[allow(dead_code)] // Helper methods for future use impl CliError { /// Create a connection failed error pub fn connection_failed(endpoint: impl Into, reason: impl Into) -> Self { @@ -148,6 +149,17 @@ impl CliError { } } + /// Create an invalid JSON error + pub fn invalid_json(input: impl Into, error: impl fmt::Display) -> Self { + Self::InvalidJson { + input: input.into(), + error: error.to_string(), + } + } +} + +#[cfg(test)] +impl CliError { /// Create a record not found error pub fn record_not_found(name: impl Into) -> Self { Self::RecordNotFound { name: name.into() } @@ -174,14 +186,6 @@ impl CliError { } } - /// Create an invalid JSON error - pub fn invalid_json(input: impl Into, error: impl fmt::Display) -> Self { - Self::InvalidJson { - input: input.into(), - error: error.to_string(), - } - } - /// Create a server error from protocol response pub fn server_error( code: impl Into, diff --git a/tools/aimdb-mcp/src/server.rs b/tools/aimdb-mcp/src/server.rs index d552bad4..17e7e215 100644 --- a/tools/aimdb-mcp/src/server.rs +++ b/tools/aimdb-mcp/src/server.rs @@ -100,7 +100,6 @@ impl McpServer { } /// Set server state (internal use) - #[allow(dead_code)] pub(crate) async fn set_state(&self, new_state: ServerState) { *self.state.lock().await = new_state; }