1use crate::{
2 error::check,
3 qos::Qos,
4 xtypes::{FindScope, TopicDescriptor, TypeInfo, TypeObject},
5 DdsError, DdsResult, DdsType, UntypedTopic,
6};
7use cyclonedds_rust_sys::*;
8use std::ffi::{c_char, CStr};
9
10pub const DDS_MIN_PSEUDO_HANDLE: dds_entity_t = 0x7fff0000u32 as dds_entity_t;
11pub const BUILTIN_TOPIC_DCPSPARTICIPANT: dds_entity_t = DDS_MIN_PSEUDO_HANDLE + 1;
12pub const BUILTIN_TOPIC_DCPSTOPIC: dds_entity_t = DDS_MIN_PSEUDO_HANDLE + 2;
13pub const BUILTIN_TOPIC_DCPSPUBLICATION: dds_entity_t = DDS_MIN_PSEUDO_HANDLE + 3;
14pub const BUILTIN_TOPIC_DCPSSUBSCRIPTION: dds_entity_t = DDS_MIN_PSEUDO_HANDLE + 4;
15
16fn dup_cstr(ptr: *const c_char) -> *mut c_char {
17 if ptr.is_null() {
18 std::ptr::null_mut()
19 } else {
20 unsafe { dds_string_dup(ptr) }
21 }
22}
23
24#[repr(C)]
25pub struct BuiltinParticipantSample {
26 key: dds_guid_t,
27 qos: *mut dds_qos_t,
28}
29
30unsafe impl Send for BuiltinParticipantSample {}
31
32impl BuiltinParticipantSample {
33 pub fn key(&self) -> dds_guid_t {
34 self.key
35 }
36
37 pub fn qos(&self) -> DdsResult<Option<Qos>> {
38 Qos::from_raw_clone(self.qos)
39 }
40
41 pub fn participant_name(&self) -> Option<String> {
47 self.qos()
48 .ok()
49 .flatten()
50 .and_then(|q| q.entity_name().ok().flatten())
51 }
52}
53
54impl Clone for BuiltinParticipantSample {
55 fn clone(&self) -> Self {
56 Self {
57 key: self.key,
58 qos: Qos::from_raw_clone(self.qos)
59 .ok()
60 .flatten()
61 .map_or(std::ptr::null_mut(), |q| {
62 let ptr = q.as_ptr() as *mut dds_qos_t;
63 std::mem::forget(q);
64 ptr
65 }),
66 }
67 }
68}
69
70impl Drop for BuiltinParticipantSample {
71 fn drop(&mut self) {
72 if !self.qos.is_null() {
73 unsafe { dds_delete_qos(self.qos) };
74 }
75 }
76}
77
78impl DdsType for BuiltinParticipantSample {
79 fn type_name() -> &'static str {
80 "DCPSParticipant"
81 }
82
83 fn ops() -> Vec<u32> {
84 Vec::new()
85 }
86
87 unsafe fn clone_out(ptr: *const Self) -> Self {
88 let src = &*ptr;
89 src.clone()
90 }
91}
92
93#[repr(C)]
94pub struct BuiltinTopicSample {
95 key: dds_builtintopic_topic_key_t,
96 topic_name: *mut c_char,
97 type_name: *mut c_char,
98 qos: *mut dds_qos_t,
99}
100
101unsafe impl Send for BuiltinTopicSample {}
102
103impl BuiltinTopicSample {
104 pub fn key(&self) -> dds_builtintopic_topic_key_t {
105 self.key
106 }
107
108 pub fn topic_name(&self) -> String {
109 if self.topic_name.is_null() {
110 String::new()
111 } else {
112 unsafe { CStr::from_ptr(self.topic_name) }
113 .to_string_lossy()
114 .into_owned()
115 }
116 }
117
118 pub fn type_name_value(&self) -> String {
119 if self.type_name.is_null() {
120 String::new()
121 } else {
122 unsafe { CStr::from_ptr(self.type_name) }
123 .to_string_lossy()
124 .into_owned()
125 }
126 }
127
128 pub fn qos(&self) -> DdsResult<Option<Qos>> {
129 Qos::from_raw_clone(self.qos)
130 }
131
132 pub fn find_topic(
133 &self,
134 participant: dds_entity_t,
135 scope: FindScope,
136 timeout: dds_duration_t,
137 ) -> DdsResult<Option<UntypedTopic>> {
138 let name = std::ffi::CString::new(self.topic_name())
139 .map_err(|_| DdsError::BadParameter("topic name contains null".into()))?;
140 let handle = unsafe {
141 dds_find_topic(
142 scope.as_raw(),
143 participant,
144 name.as_ptr(),
145 std::ptr::null(),
146 timeout,
147 )
148 };
149 if handle == 0 {
150 return Ok(None);
151 }
152 crate::error::check_entity(handle).map(|entity| Some(UntypedTopic::from_entity(entity)))
153 }
154}
155
156impl Clone for BuiltinTopicSample {
157 fn clone(&self) -> Self {
158 Self {
159 key: self.key,
160 topic_name: dup_cstr(self.topic_name),
161 type_name: dup_cstr(self.type_name),
162 qos: Qos::from_raw_clone(self.qos)
163 .ok()
164 .flatten()
165 .map_or(std::ptr::null_mut(), |q| {
166 let ptr = q.as_ptr() as *mut dds_qos_t;
167 std::mem::forget(q);
168 ptr
169 }),
170 }
171 }
172}
173
174impl Drop for BuiltinTopicSample {
175 fn drop(&mut self) {
176 if !self.topic_name.is_null() {
177 unsafe { dds_string_free(self.topic_name) };
178 }
179 if !self.type_name.is_null() {
180 unsafe { dds_string_free(self.type_name) };
181 }
182 if !self.qos.is_null() {
183 unsafe { dds_delete_qos(self.qos) };
184 }
185 }
186}
187
188impl DdsType for BuiltinTopicSample {
189 fn type_name() -> &'static str {
190 "DCPSTopic"
191 }
192
193 fn ops() -> Vec<u32> {
194 Vec::new()
195 }
196
197 unsafe fn clone_out(ptr: *const Self) -> Self {
198 let src = &*ptr;
199 src.clone()
200 }
201}
202
203impl std::fmt::Debug for BuiltinParticipantSample {
204 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205 f.debug_struct("BuiltinParticipantSample")
206 .field("key", &self.key)
207 .field("participant_name", &self.participant_name())
208 .finish()
209 }
210}
211
212impl std::fmt::Debug for BuiltinTopicSample {
213 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
214 f.debug_struct("BuiltinTopicSample")
215 .field("topic_name", &self.topic_name())
216 .field("type_name", &self.type_name_value())
217 .finish()
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224 use std::ffi::CString;
225
226 #[test]
227 fn builtin_topic_sample_clone_preserves_strings_and_qos() {
228 let qos = Qos::builder()
229 .reliable()
230 .entity_name("builtin-topic")
231 .build()
232 .unwrap();
233 let topic_name = CString::new("topic-a").unwrap();
234 let type_name = CString::new("type-a").unwrap();
235
236 let sample = BuiltinTopicSample {
237 key: dds_builtintopic_topic_key_t { d: [1; 16] },
238 topic_name: unsafe { dds_string_dup(topic_name.as_ptr()) },
239 type_name: unsafe { dds_string_dup(type_name.as_ptr()) },
240 qos: {
241 let ptr = qos.as_ptr() as *mut dds_qos_t;
242 std::mem::forget(qos);
243 ptr
244 },
245 };
246
247 let cloned = sample.clone();
248 assert_eq!(cloned.topic_name(), "topic-a");
249 assert_eq!(cloned.type_name_value(), "type-a");
250 let cloned_qos = cloned.qos().unwrap().unwrap();
251 assert_eq!(cloned_qos.entity_name().unwrap().unwrap(), "builtin-topic");
252 }
253
254 #[test]
255 fn builtin_participant_sample_clone_preserves_qos() {
256 let qos = Qos::builder()
257 .entity_name("builtin-participant")
258 .build()
259 .unwrap();
260 let sample = BuiltinParticipantSample {
261 key: dds_guid_t { v: [9; 16] },
262 qos: {
263 let ptr = qos.as_ptr() as *mut dds_qos_t;
264 std::mem::forget(qos);
265 ptr
266 },
267 };
268
269 let cloned = sample.clone();
270 let cloned_qos = cloned.qos().unwrap().unwrap();
271 assert_eq!(
272 cloned_qos.entity_name().unwrap().unwrap(),
273 "builtin-participant"
274 );
275 assert_eq!(cloned.key().v, [9; 16]);
276 }
277}
278
279#[repr(C)]
280pub struct BuiltinEndpointSample {
281 key: dds_guid_t,
282 participant_key: dds_guid_t,
283 participant_instance_handle: dds_instance_handle_t,
284 topic_name: *mut c_char,
285 type_name: *mut c_char,
286 qos: *mut dds_qos_t,
287}
288
289unsafe impl Send for BuiltinEndpointSample {}
290
291impl BuiltinEndpointSample {
292 fn as_native_ptr(&self) -> *mut dds_builtintopic_endpoint_t {
293 self as *const Self as *mut dds_builtintopic_endpoint_t
294 }
295
296 pub fn key(&self) -> dds_guid_t {
297 self.key
298 }
299
300 pub fn participant_key(&self) -> dds_guid_t {
301 self.participant_key
302 }
303
304 pub fn participant_instance_handle(&self) -> dds_instance_handle_t {
305 self.participant_instance_handle
306 }
307
308 pub fn topic_name(&self) -> String {
309 if self.topic_name.is_null() {
310 String::new()
311 } else {
312 unsafe { CStr::from_ptr(self.topic_name) }
313 .to_string_lossy()
314 .into_owned()
315 }
316 }
317
318 pub fn type_name_value(&self) -> String {
319 if self.type_name.is_null() {
320 String::new()
321 } else {
322 unsafe { CStr::from_ptr(self.type_name) }
323 .to_string_lossy()
324 .into_owned()
325 }
326 }
327
328 pub fn type_name(&self) -> String {
331 self.type_name_value()
332 }
333
334 pub fn qos(&self) -> DdsResult<Option<Qos>> {
335 Qos::from_raw_clone(self.qos)
336 }
337
338 pub fn type_info(&self) -> DdsResult<TypeInfo> {
339 let mut ptr = std::ptr::null();
340 unsafe {
341 check(dds_builtintopic_get_endpoint_type_info(
342 self.as_native_ptr(),
343 &mut ptr,
344 ))?;
345 }
346 if ptr.is_null() {
347 return Err(DdsError::Other(
348 "CycloneDDS returned null endpoint typeinfo".into(),
349 ));
350 }
351 Ok(TypeInfo::from_raw(ptr.cast_mut()))
352 }
353
354 pub fn create_topic_descriptor(
355 &self,
356 participant: dds_entity_t,
357 scope: FindScope,
358 timeout: dds_duration_t,
359 ) -> DdsResult<TopicDescriptor> {
360 self.type_info()?
361 .create_topic_descriptor(participant, scope, timeout)
362 }
363
364 pub fn create_topic(
365 &self,
366 participant: dds_entity_t,
367 scope: FindScope,
368 timeout: dds_duration_t,
369 ) -> DdsResult<UntypedTopic> {
370 let descriptor = self.create_topic_descriptor(participant, scope, timeout)?;
371 descriptor.create_topic(participant, &self.topic_name())
372 }
373
374 pub fn create_topic_with_qos(
375 &self,
376 participant: dds_entity_t,
377 scope: FindScope,
378 timeout: dds_duration_t,
379 qos: &Qos,
380 ) -> DdsResult<UntypedTopic> {
381 let descriptor = self.create_topic_descriptor(participant, scope, timeout)?;
382 descriptor.create_topic_with_qos(participant, &self.topic_name(), qos)
383 }
384
385 pub fn find_topic(
386 &self,
387 participant: dds_entity_t,
388 scope: FindScope,
389 timeout: dds_duration_t,
390 ) -> DdsResult<Option<UntypedTopic>> {
391 let type_info = self.type_info()?;
392 let name = std::ffi::CString::new(self.topic_name())
393 .map_err(|_| DdsError::BadParameter("topic name contains null".into()))?;
394 let handle = unsafe {
395 dds_find_topic(
396 scope.as_raw(),
397 participant,
398 name.as_ptr(),
399 type_info.as_ptr(),
400 timeout,
401 )
402 };
403 if handle == 0 {
404 return Ok(None);
405 }
406 crate::error::check_entity(handle).map(|entity| Some(UntypedTopic::from_entity(entity)))
407 }
408
409 pub fn minimal_type_object(
410 &self,
411 participant: dds_entity_t,
412 timeout: dds_duration_t,
413 ) -> DdsResult<Option<TypeObject>> {
414 self.type_info()?.minimal_type_object(participant, timeout)
415 }
416
417 pub fn complete_type_object(
418 &self,
419 participant: dds_entity_t,
420 timeout: dds_duration_t,
421 ) -> DdsResult<Option<TypeObject>> {
422 self.type_info()?.complete_type_object(participant, timeout)
423 }
424}
425
426impl Clone for BuiltinEndpointSample {
427 fn clone(&self) -> Self {
428 Self {
429 key: self.key,
430 participant_key: self.participant_key,
431 participant_instance_handle: self.participant_instance_handle,
432 topic_name: dup_cstr(self.topic_name),
433 type_name: dup_cstr(self.type_name),
434 qos: Qos::from_raw_clone(self.qos)
435 .ok()
436 .flatten()
437 .map_or(std::ptr::null_mut(), |q| {
438 let ptr = q.as_ptr() as *mut dds_qos_t;
439 std::mem::forget(q);
440 ptr
441 }),
442 }
443 }
444}
445
446impl Drop for BuiltinEndpointSample {
447 fn drop(&mut self) {
448 if !self.topic_name.is_null() {
449 unsafe { dds_string_free(self.topic_name) };
450 }
451 if !self.type_name.is_null() {
452 unsafe { dds_string_free(self.type_name) };
453 }
454 if !self.qos.is_null() {
455 unsafe { dds_delete_qos(self.qos) };
456 }
457 }
458}
459
460impl DdsType for BuiltinEndpointSample {
461 fn type_name() -> &'static str {
462 "BuiltinEndpointSample"
463 }
464
465 fn ops() -> Vec<u32> {
466 Vec::new()
467 }
468
469 unsafe fn clone_out(ptr: *const Self) -> Self {
470 let src = &*ptr;
471 src.clone()
472 }
473}
474
475impl std::fmt::Debug for BuiltinEndpointSample {
476 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
477 f.debug_struct("BuiltinEndpointSample")
478 .field("topic_name", &self.topic_name())
479 .field("type_name", &self.type_name_value())
480 .finish()
481 }
482}
483
484#[cfg(test)]
485mod endpoint_tests {
486 use super::*;
487 use std::ffi::CString;
488
489 #[test]
490 fn builtin_endpoint_sample_clone_preserves_strings_and_qos() {
491 let qos = Qos::builder()
492 .reliable()
493 .entity_name("builtin-endpoint")
494 .build()
495 .unwrap();
496 let topic_name = CString::new("topic-endpoint").unwrap();
497 let type_name = CString::new("type-endpoint").unwrap();
498
499 let sample = BuiltinEndpointSample {
500 key: dds_guid_t { v: [1; 16] },
501 participant_key: dds_guid_t { v: [2; 16] },
502 participant_instance_handle: 42,
503 topic_name: unsafe { dds_string_dup(topic_name.as_ptr()) },
504 type_name: unsafe { dds_string_dup(type_name.as_ptr()) },
505 qos: {
506 let ptr = qos.as_ptr() as *mut dds_qos_t;
507 std::mem::forget(qos);
508 ptr
509 },
510 };
511
512 let cloned = sample.clone();
513 assert_eq!(cloned.topic_name(), "topic-endpoint");
514 assert_eq!(cloned.type_name_value(), "type-endpoint");
515 assert_eq!(cloned.participant_instance_handle(), 42);
516 let cloned_qos = cloned.qos().unwrap().unwrap();
517 assert_eq!(
518 cloned_qos.entity_name().unwrap().unwrap(),
519 "builtin-endpoint"
520 );
521 }
522}