Skip to main content

velo_common/
transport.rs

1// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Transport key type for type-safe transport identification.
5
6use std::fmt;
7use std::sync::Arc;
8
9/// A type-safe wrapper around transport keys for WorkerAddress.
10///
11/// This provides a zero-cost abstraction over `Arc<str>` with type safety
12/// to prevent accidentally mixing transport keys with other string types.
13///
14/// # Examples
15///
16/// ```
17/// use velo_common::TransportKey;
18///
19/// let key = TransportKey::new("tcp");
20/// assert_eq!(key.as_str(), "tcp");
21///
22/// // Ergonomic conversions
23/// let key2: TransportKey = "rdma".into();
24/// let key3 = TransportKey::from("udp");
25///
26/// // Use in collections
27/// use std::collections::HashMap;
28/// let mut transports = HashMap::new();
29/// transports.insert(TransportKey::from("tcp"), "tcp://127.0.0.1:5555");
30/// ```
31#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
32pub struct TransportKey(Arc<str>);
33
34impl TransportKey {
35    /// Create a new TransportKey from any type that can be converted into `Arc<str>`.
36    pub fn new(key: impl Into<Arc<str>>) -> Self {
37        Self(key.into())
38    }
39
40    /// Get the key as a string slice.
41    pub fn as_str(&self) -> &str {
42        &self.0
43    }
44}
45
46// Deref to str for ergonomic usage
47impl std::ops::Deref for TransportKey {
48    type Target = str;
49
50    fn deref(&self) -> &Self::Target {
51        &self.0
52    }
53}
54
55// AsRef for flexible parameter types
56impl AsRef<str> for TransportKey {
57    fn as_ref(&self) -> &str {
58        &self.0
59    }
60}
61
62// From conversions for ergonomic construction
63impl From<&str> for TransportKey {
64    fn from(s: &str) -> Self {
65        Self(Arc::from(s))
66    }
67}
68
69impl From<String> for TransportKey {
70    fn from(s: String) -> Self {
71        Self(Arc::from(s))
72    }
73}
74
75impl From<Arc<str>> for TransportKey {
76    fn from(s: Arc<str>) -> Self {
77        Self(s)
78    }
79}
80
81impl From<&String> for TransportKey {
82    fn from(s: &String) -> Self {
83        Self(Arc::from(s.as_str()))
84    }
85}
86
87impl From<TransportKey> for String {
88    fn from(val: TransportKey) -> Self {
89        val.0.to_string()
90    }
91}
92
93// Borrow trait for HashMap lookups with &str
94impl std::borrow::Borrow<str> for TransportKey {
95    fn borrow(&self) -> &str {
96        &self.0
97    }
98}
99
100// Display for printing
101impl fmt::Display for TransportKey {
102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        write!(f, "{}", self.0)
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110    use std::collections::{HashMap, HashSet};
111
112    #[test]
113    fn test_transport_key_creation() {
114        // Test new() method
115        let key1 = TransportKey::new("tcp");
116        assert_eq!(key1.as_str(), "tcp");
117
118        // Test From<&str>
119        let key2: TransportKey = "rdma".into();
120        assert_eq!(key2.as_str(), "rdma");
121
122        // Test From<String>
123        let key3 = TransportKey::from(String::from("udp"));
124        assert_eq!(key3.as_str(), "udp");
125
126        // Test From<&String>
127        let s = String::from("grpc");
128        let key4 = TransportKey::from(&s);
129        assert_eq!(key4.as_str(), "grpc");
130
131        // Test From<Arc<str>>
132        let arc_str: Arc<str> = Arc::from("http");
133        let key5 = TransportKey::from(arc_str);
134        assert_eq!(key5.as_str(), "http");
135    }
136
137    #[test]
138    fn test_transport_key_deref() {
139        let key = TransportKey::from("tcp");
140
141        // Deref to str methods should work
142        assert_eq!(key.len(), 3);
143        assert_eq!(key.chars().count(), 3);
144        assert!(key.starts_with("tc"));
145        assert!(key.ends_with("cp"));
146
147        // Can use str slicing through Deref
148        assert_eq!(&key[0..2], "tc");
149    }
150
151    #[test]
152    fn test_transport_key_as_ref() {
153        let key = TransportKey::from("tcp");
154
155        // AsRef<str> allows passing to functions expecting &str
156        fn takes_str_ref(s: &str) -> usize {
157            s.len()
158        }
159
160        assert_eq!(takes_str_ref(&key), 3);
161        assert_eq!(takes_str_ref(key.as_ref()), 3);
162    }
163
164    #[test]
165    fn test_transport_key_display() {
166        let key = TransportKey::from("tcp");
167        assert_eq!(format!("{}", key), "tcp");
168        assert_eq!(key.to_string(), "tcp");
169    }
170
171    #[test]
172    fn test_transport_key_debug() {
173        let key = TransportKey::from("tcp");
174        let debug_str = format!("{:?}", key);
175        assert!(debug_str.contains("TransportKey"));
176        assert!(debug_str.contains("tcp"));
177    }
178
179    #[test]
180    fn test_transport_key_equality() {
181        let key1 = TransportKey::from("tcp");
182        let key2 = TransportKey::from("tcp");
183        let key3 = TransportKey::from("rdma");
184
185        assert_eq!(key1, key2);
186        assert_ne!(key1, key3);
187
188        // Test with different source types
189        let key4: TransportKey = String::from("tcp").into();
190        assert_eq!(key1, key4);
191    }
192
193    #[test]
194    fn test_transport_key_ordering() {
195        let mut keys = [
196            TransportKey::from("udp"),
197            TransportKey::from("tcp"),
198            TransportKey::from("rdma"),
199            TransportKey::from("grpc"),
200        ];
201
202        keys.sort();
203
204        assert_eq!(keys[0], TransportKey::from("grpc"));
205        assert_eq!(keys[1], TransportKey::from("rdma"));
206        assert_eq!(keys[2], TransportKey::from("tcp"));
207        assert_eq!(keys[3], TransportKey::from("udp"));
208    }
209
210    #[test]
211    fn test_transport_key_hash() {
212        let mut set = HashSet::new();
213        set.insert(TransportKey::from("tcp"));
214        set.insert(TransportKey::from("rdma"));
215        set.insert(TransportKey::from("tcp")); // Duplicate
216
217        assert_eq!(set.len(), 2);
218        assert!(set.contains(&TransportKey::from("tcp")));
219        assert!(set.contains(&TransportKey::from("rdma")));
220        assert!(!set.contains(&TransportKey::from("udp")));
221    }
222
223    #[test]
224    fn test_transport_key_in_hashmap() {
225        let mut map = HashMap::new();
226        map.insert(TransportKey::from("tcp"), "tcp://127.0.0.1:5555");
227        map.insert(TransportKey::from("rdma"), "rdma://10.0.0.1:6666");
228
229        // Can lookup with TransportKey
230        assert_eq!(
231            map.get(&TransportKey::from("tcp")),
232            Some(&"tcp://127.0.0.1:5555")
233        );
234
235        // Can lookup with &str via Borrow trait
236        assert_eq!(map.get("tcp"), Some(&"tcp://127.0.0.1:5555"));
237        assert_eq!(map.get("rdma"), Some(&"rdma://10.0.0.1:6666"));
238        assert_eq!(map.get("udp"), None);
239    }
240
241    #[test]
242    fn test_transport_key_clone() {
243        let key1 = TransportKey::from("tcp");
244        let key2 = key1.clone();
245
246        assert_eq!(key1, key2);
247        assert_eq!(key1.as_str(), key2.as_str());
248
249        // Verify Arc is shared (same pointer)
250        let ptr1 = key1.as_str().as_ptr();
251        let ptr2 = key2.as_str().as_ptr();
252        assert_eq!(ptr1, ptr2);
253    }
254}