zerodds_discovery/type_lookup/
endpoints.rs1use alloc::format;
18use alloc::string::String;
19
20use zerodds_rtps::wire_types::{EntityId, Guid, GuidPrefix};
21
22pub const TYPELOOKUP_TOPIC_PREFIX: &str = "dds.builtin.TOS";
24
25#[must_use]
34pub fn format_service_instance_name(prefix: &GuidPrefix) -> String {
35 let mut hex = String::with_capacity(24);
36 for b in prefix.0.iter() {
37 hex.push_str(&format!("{b:02x}"));
38 }
39 format!("{TYPELOOKUP_TOPIC_PREFIX}.{hex}")
40}
41
42#[must_use]
46pub fn format_service_instance_name_short(prefix: &GuidPrefix) -> String {
47 let mut hex = String::with_capacity(16);
48 for b in prefix.0[..8].iter() {
49 hex.push_str(&format!("{b:02x}"));
50 }
51 format!("{TYPELOOKUP_TOPIC_PREFIX}.{hex}")
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub struct TypeLookupEndpoints {
61 pub prefix: GuidPrefix,
63}
64
65impl TypeLookupEndpoints {
66 #[must_use]
68 pub fn new(prefix: GuidPrefix) -> Self {
69 Self { prefix }
70 }
71
72 #[must_use]
74 pub fn request_writer(&self) -> Guid {
75 Guid::new(self.prefix, EntityId::TL_SVC_REQ_WRITER)
76 }
77
78 #[must_use]
80 pub fn request_reader(&self) -> Guid {
81 Guid::new(self.prefix, EntityId::TL_SVC_REQ_READER)
82 }
83
84 #[must_use]
86 pub fn reply_writer(&self) -> Guid {
87 Guid::new(self.prefix, EntityId::TL_SVC_REPLY_WRITER)
88 }
89
90 #[must_use]
92 pub fn reply_reader(&self) -> Guid {
93 Guid::new(self.prefix, EntityId::TL_SVC_REPLY_READER)
94 }
95
96 #[must_use]
98 pub fn service_instance_name(&self) -> String {
99 format_service_instance_name(&self.prefix)
100 }
101
102 #[must_use]
105 pub fn all_guids(&self) -> [Guid; 4] {
106 [
107 self.request_writer(),
108 self.request_reader(),
109 self.reply_writer(),
110 self.reply_reader(),
111 ]
112 }
113}
114
115#[cfg(test)]
116#[allow(clippy::unwrap_used)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn service_instance_name_format() {
122 let prefix = GuidPrefix::from_bytes([
123 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
124 ]);
125 let name = format_service_instance_name(&prefix);
126 assert_eq!(name, "dds.builtin.TOS.0102030405060708090a0b0c");
127 assert!(name.starts_with(TYPELOOKUP_TOPIC_PREFIX));
128 }
129
130 #[test]
131 fn service_instance_name_short_takes_first_8_bytes() {
132 let prefix = GuidPrefix::from_bytes([
133 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
134 ]);
135 let name = format_service_instance_name_short(&prefix);
136 assert_eq!(name, "dds.builtin.TOS.aabbccddeeff0011");
137 }
138
139 #[test]
140 fn endpoints_have_distinct_entity_ids() {
141 let e = TypeLookupEndpoints::new(GuidPrefix::from_bytes([0; 12]));
142 let g = e.all_guids();
143 for i in 0..g.len() {
144 for j in (i + 1)..g.len() {
145 assert_ne!(g[i].entity_id, g[j].entity_id);
146 }
147 }
148 }
149
150 #[test]
151 fn endpoints_share_prefix() {
152 let prefix = GuidPrefix::from_bytes([0xAA; 12]);
153 let e = TypeLookupEndpoints::new(prefix);
154 for g in e.all_guids() {
155 assert_eq!(g.prefix, prefix);
156 }
157 }
158
159 #[test]
160 fn entity_ids_match_spec_constants() {
161 let e = TypeLookupEndpoints::new(GuidPrefix::from_bytes([1; 12]));
162 assert_eq!(e.request_writer().entity_id, EntityId::TL_SVC_REQ_WRITER);
163 assert_eq!(e.request_reader().entity_id, EntityId::TL_SVC_REQ_READER);
164 assert_eq!(e.reply_writer().entity_id, EntityId::TL_SVC_REPLY_WRITER);
165 assert_eq!(e.reply_reader().entity_id, EntityId::TL_SVC_REPLY_READER);
166 }
167
168 #[test]
169 fn service_instance_name_via_endpoints() {
170 let prefix = GuidPrefix::from_bytes([0x00; 12]);
171 let e = TypeLookupEndpoints::new(prefix);
172 assert_eq!(
173 e.service_instance_name(),
174 format_service_instance_name(&prefix)
175 );
176 }
177}