// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 //! Transport key type for type-safe transport identification. use std::fmt; use std::sync::Arc; /// A type-safe wrapper around transport keys for WorkerAddress. /// /// This provides a zero-cost abstraction over `Arc` with type safety /// to prevent accidentally mixing transport keys with other string types. /// /// # Examples /// /// ``` /// use velo_common::TransportKey; /// /// let key = TransportKey::new("tcp"); /// assert_eq!(key.as_str(), "tcp"); /// /// // Ergonomic conversions /// let key2: TransportKey = "rdma".into(); /// let key3 = TransportKey::from("udp"); /// /// // Use in collections /// use std::collections::HashMap; /// let mut transports = HashMap::new(); /// transports.insert(TransportKey::from("tcp"), "tcp://127.0.0.1:5555"); /// ``` #[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct TransportKey(Arc); impl TransportKey { /// Create a new TransportKey from any type that can be converted into Arc. pub fn new(key: impl Into>) -> Self { Self(key.into()) } /// Get the key as a string slice. pub fn as_str(&self) -> &str { &self.0 } } // Deref to str for ergonomic usage impl std::ops::Deref for TransportKey { type Target = str; fn deref(&self) -> &Self::Target { &self.0 } } // AsRef for flexible parameter types impl AsRef for TransportKey { fn as_ref(&self) -> &str { &self.0 } } // From conversions for ergonomic construction impl From<&str> for TransportKey { fn from(s: &str) -> Self { Self(Arc::from(s)) } } impl From for TransportKey { fn from(s: String) -> Self { Self(Arc::from(s)) } } impl From> for TransportKey { fn from(s: Arc) -> Self { Self(s) } } impl From<&String> for TransportKey { fn from(s: &String) -> Self { Self(Arc::from(s.as_str())) } } impl From for String { fn from(val: TransportKey) -> Self { val.0.to_string() } } // Borrow trait for HashMap lookups with &str impl std::borrow::Borrow for TransportKey { fn borrow(&self) -> &str { &self.0 } } // Display for printing impl fmt::Display for TransportKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } #[cfg(test)] mod tests { use super::*; use std::collections::{HashMap, HashSet}; #[test] fn test_transport_key_creation() { // Test new() method let key1 = TransportKey::new("tcp"); assert_eq!(key1.as_str(), "tcp"); // Test From<&str> let key2: TransportKey = "rdma".into(); assert_eq!(key2.as_str(), "rdma"); // Test From let key3 = TransportKey::from(String::from("udp")); assert_eq!(key3.as_str(), "udp"); // Test From<&String> let s = String::from("grpc"); let key4 = TransportKey::from(&s); assert_eq!(key4.as_str(), "grpc"); // Test From> let arc_str: Arc = Arc::from("http"); let key5 = TransportKey::from(arc_str); assert_eq!(key5.as_str(), "http"); } #[test] fn test_transport_key_deref() { let key = TransportKey::from("tcp"); // Deref to str methods should work assert_eq!(key.len(), 3); assert_eq!(key.chars().count(), 3); assert!(key.starts_with("tc")); assert!(key.ends_with("cp")); // Can use str slicing through Deref assert_eq!(&key[0..2], "tc"); } #[test] fn test_transport_key_as_ref() { let key = TransportKey::from("tcp"); // AsRef allows passing to functions expecting &str fn takes_str_ref(s: &str) -> usize { s.len() } assert_eq!(takes_str_ref(&key), 3); assert_eq!(takes_str_ref(key.as_ref()), 3); } #[test] fn test_transport_key_display() { let key = TransportKey::from("tcp"); assert_eq!(format!("{}", key), "tcp"); assert_eq!(key.to_string(), "tcp"); } #[test] fn test_transport_key_debug() { let key = TransportKey::from("tcp"); let debug_str = format!("{:?}", key); assert!(debug_str.contains("TransportKey")); assert!(debug_str.contains("tcp")); } #[test] fn test_transport_key_equality() { let key1 = TransportKey::from("tcp"); let key2 = TransportKey::from("tcp"); let key3 = TransportKey::from("rdma"); assert_eq!(key1, key2); assert_ne!(key1, key3); // Test with different source types let key4: TransportKey = String::from("tcp").into(); assert_eq!(key1, key4); } #[test] fn test_transport_key_ordering() { let mut keys = [ TransportKey::from("udp"), TransportKey::from("tcp"), TransportKey::from("rdma"), TransportKey::from("grpc"), ]; keys.sort(); assert_eq!(keys[0], TransportKey::from("grpc")); assert_eq!(keys[1], TransportKey::from("rdma")); assert_eq!(keys[2], TransportKey::from("tcp")); assert_eq!(keys[3], TransportKey::from("udp")); } #[test] fn test_transport_key_hash() { let mut set = HashSet::new(); set.insert(TransportKey::from("tcp")); set.insert(TransportKey::from("rdma")); set.insert(TransportKey::from("tcp")); // Duplicate assert_eq!(set.len(), 2); assert!(set.contains(&TransportKey::from("tcp"))); assert!(set.contains(&TransportKey::from("rdma"))); assert!(!set.contains(&TransportKey::from("udp"))); } #[test] fn test_transport_key_in_hashmap() { let mut map = HashMap::new(); map.insert(TransportKey::from("tcp"), "tcp://127.0.0.1:5555"); map.insert(TransportKey::from("rdma"), "rdma://10.0.0.1:6666"); // Can lookup with TransportKey assert_eq!( map.get(&TransportKey::from("tcp")), Some(&"tcp://127.0.0.1:5555") ); // Can lookup with &str via Borrow trait assert_eq!(map.get("tcp"), Some(&"tcp://127.0.0.1:5555")); assert_eq!(map.get("rdma"), Some(&"rdma://10.0.0.1:6666")); assert_eq!(map.get("udp"), None); } #[test] fn test_transport_key_clone() { let key1 = TransportKey::from("tcp"); let key2 = key1.clone(); assert_eq!(key1, key2); assert_eq!(key1.as_str(), key2.as_str()); // Verify Arc is shared (same pointer) let ptr1 = key1.as_str().as_ptr(); let ptr2 = key2.as_str().as_ptr(); assert_eq!(ptr1, ptr2); } }