1use std::ffi::CStr;
9use std::os::raw::c_char;
10use std::ptr;
11
12use hdds::api::QoS;
13
14use crate::HddsError;
15
16#[repr(C)]
18pub struct HddsQoS {
19 _private: [u8; 0],
20}
21
22#[no_mangle]
31pub unsafe extern "C" fn hdds_qos_default() -> *mut HddsQoS {
32 let qos = Box::new(QoS::default());
33 Box::into_raw(qos).cast::<HddsQoS>()
34}
35
36#[no_mangle]
43pub unsafe extern "C" fn hdds_qos_best_effort() -> *mut HddsQoS {
44 let qos = Box::new(QoS::best_effort());
45 Box::into_raw(qos).cast::<HddsQoS>()
46}
47
48#[no_mangle]
55pub unsafe extern "C" fn hdds_qos_reliable() -> *mut HddsQoS {
56 let qos = Box::new(QoS::reliable());
57 Box::into_raw(qos).cast::<HddsQoS>()
58}
59
60#[no_mangle]
67pub unsafe extern "C" fn hdds_qos_rti_defaults() -> *mut HddsQoS {
68 let qos = Box::new(QoS::rti_defaults());
69 Box::into_raw(qos).cast::<HddsQoS>()
70}
71
72#[no_mangle]
78pub unsafe extern "C" fn hdds_qos_destroy(qos: *mut HddsQoS) {
79 if !qos.is_null() {
80 let _ = Box::from_raw(qos.cast::<QoS>());
81 }
82}
83
84#[cfg(feature = "qos-loaders")]
104#[no_mangle]
105pub unsafe extern "C" fn hdds_qos_load_fastdds_xml(path: *const c_char) -> *mut HddsQoS {
106 if path.is_null() {
107 return ptr::null_mut();
108 }
109
110 let Ok(path_str) = CStr::from_ptr(path).to_str() else {
111 return ptr::null_mut();
112 };
113
114 match QoS::load_fastdds(path_str) {
115 Ok(qos) => Box::into_raw(Box::new(qos)).cast::<HddsQoS>(),
116 Err(err) => {
117 eprintln!("[hdds_c] Failed to load FastDDS XML: {}", err);
118 ptr::null_mut()
119 }
120 }
121}
122
123#[cfg(feature = "qos-loaders")]
139#[no_mangle]
140pub unsafe extern "C" fn hdds_qos_from_xml(path: *const c_char) -> *mut HddsQoS {
141 if path.is_null() {
142 return ptr::null_mut();
143 }
144
145 let Ok(path_str) = CStr::from_ptr(path).to_str() else {
146 return ptr::null_mut();
147 };
148
149 match QoS::from_xml(path_str) {
150 Ok(qos) => Box::into_raw(Box::new(qos)).cast::<HddsQoS>(),
151 Err(err) => {
152 eprintln!("[hdds_c] Failed to load QoS from XML: {}", err);
153 ptr::null_mut()
154 }
155 }
156}
157
158#[no_mangle]
167pub unsafe extern "C" fn hdds_qos_set_history_depth(qos: *mut HddsQoS, depth: u32) -> HddsError {
168 if qos.is_null() {
169 return HddsError::HddsInvalidArgument;
170 }
171
172 let qos_ref = &mut *qos.cast::<QoS>();
173 qos_ref.history = hdds::dds::qos::History::KeepLast(depth);
174 HddsError::HddsOk
175}
176
177#[no_mangle]
182pub unsafe extern "C" fn hdds_qos_set_history_keep_all(qos: *mut HddsQoS) -> HddsError {
183 if qos.is_null() {
184 return HddsError::HddsInvalidArgument;
185 }
186
187 let qos_ref = &mut *qos.cast::<QoS>();
188 qos_ref.history = hdds::dds::qos::History::KeepAll;
189 HddsError::HddsOk
190}
191
192#[no_mangle]
197pub unsafe extern "C" fn hdds_qos_set_volatile(qos: *mut HddsQoS) -> HddsError {
198 if qos.is_null() {
199 return HddsError::HddsInvalidArgument;
200 }
201
202 let qos_ref = &mut *qos.cast::<QoS>();
203 qos_ref.durability = hdds::dds::qos::Durability::Volatile;
204 HddsError::HddsOk
205}
206
207#[no_mangle]
212pub unsafe extern "C" fn hdds_qos_set_transient_local(qos: *mut HddsQoS) -> HddsError {
213 if qos.is_null() {
214 return HddsError::HddsInvalidArgument;
215 }
216
217 let qos_ref = &mut *qos.cast::<QoS>();
218 qos_ref.durability = hdds::dds::qos::Durability::TransientLocal;
219 HddsError::HddsOk
220}
221
222#[no_mangle]
227pub unsafe extern "C" fn hdds_qos_set_persistent(qos: *mut HddsQoS) -> HddsError {
228 if qos.is_null() {
229 return HddsError::HddsInvalidArgument;
230 }
231
232 let qos_ref = &mut *qos.cast::<QoS>();
233 qos_ref.durability = hdds::dds::qos::Durability::Persistent;
234 HddsError::HddsOk
235}
236
237#[no_mangle]
242pub unsafe extern "C" fn hdds_qos_set_reliable(qos: *mut HddsQoS) -> HddsError {
243 if qos.is_null() {
244 return HddsError::HddsInvalidArgument;
245 }
246
247 let qos_ref = &mut *qos.cast::<QoS>();
248 qos_ref.reliability = hdds::dds::qos::Reliability::Reliable;
249 HddsError::HddsOk
250}
251
252#[no_mangle]
257pub unsafe extern "C" fn hdds_qos_set_best_effort(qos: *mut HddsQoS) -> HddsError {
258 if qos.is_null() {
259 return HddsError::HddsInvalidArgument;
260 }
261
262 let qos_ref = &mut *qos.cast::<QoS>();
263 qos_ref.reliability = hdds::dds::qos::Reliability::BestEffort;
264 HddsError::HddsOk
265}
266
267#[no_mangle]
272pub unsafe extern "C" fn hdds_qos_set_deadline_ns(qos: *mut HddsQoS, period_ns: u64) -> HddsError {
273 if qos.is_null() {
274 return HddsError::HddsInvalidArgument;
275 }
276
277 let qos_ref = &mut *qos.cast::<QoS>();
278 qos_ref.deadline = hdds::dds::qos::Deadline::new(std::time::Duration::from_nanos(period_ns));
279 HddsError::HddsOk
280}
281
282#[no_mangle]
287pub unsafe extern "C" fn hdds_qos_set_lifespan_ns(
288 qos: *mut HddsQoS,
289 duration_ns: u64,
290) -> HddsError {
291 if qos.is_null() {
292 return HddsError::HddsInvalidArgument;
293 }
294
295 let qos_ref = &mut *qos.cast::<QoS>();
296 qos_ref.lifespan = hdds::dds::qos::Lifespan::new(std::time::Duration::from_nanos(duration_ns));
297 HddsError::HddsOk
298}
299
300#[no_mangle]
305pub unsafe extern "C" fn hdds_qos_set_ownership_shared(qos: *mut HddsQoS) -> HddsError {
306 if qos.is_null() {
307 return HddsError::HddsInvalidArgument;
308 }
309
310 let qos_ref = &mut *qos.cast::<QoS>();
311 qos_ref.ownership = hdds::dds::qos::Ownership::shared();
312 HddsError::HddsOk
313}
314
315#[no_mangle]
320pub unsafe extern "C" fn hdds_qos_set_ownership_exclusive(
321 qos: *mut HddsQoS,
322 strength: i32,
323) -> HddsError {
324 if qos.is_null() {
325 return HddsError::HddsInvalidArgument;
326 }
327
328 let qos_ref = &mut *qos.cast::<QoS>();
329 qos_ref.ownership = hdds::dds::qos::Ownership::exclusive();
330 qos_ref.ownership_strength = hdds::dds::qos::OwnershipStrength::new(strength);
331 HddsError::HddsOk
332}
333
334#[no_mangle]
340pub unsafe extern "C" fn hdds_qos_add_partition(
341 qos: *mut HddsQoS,
342 partition: *const c_char,
343) -> HddsError {
344 if qos.is_null() || partition.is_null() {
345 return HddsError::HddsInvalidArgument;
346 }
347
348 let Ok(part_str) = CStr::from_ptr(partition).to_str() else {
349 return HddsError::HddsInvalidArgument;
350 };
351
352 let qos_ref = &mut *qos.cast::<QoS>();
353
354 qos_ref.partition.add(part_str);
356
357 HddsError::HddsOk
358}
359
360#[no_mangle]
369pub unsafe extern "C" fn hdds_qos_is_reliable(qos: *const HddsQoS) -> bool {
370 if qos.is_null() {
371 return false;
372 }
373
374 let qos_ref = &*qos.cast::<QoS>();
375 matches!(qos_ref.reliability, hdds::dds::qos::Reliability::Reliable)
376}
377
378#[no_mangle]
383pub unsafe extern "C" fn hdds_qos_is_transient_local(qos: *const HddsQoS) -> bool {
384 if qos.is_null() {
385 return false;
386 }
387
388 let qos_ref = &*qos.cast::<QoS>();
389 matches!(
390 qos_ref.durability,
391 hdds::dds::qos::Durability::TransientLocal
392 )
393}
394
395#[no_mangle]
400pub unsafe extern "C" fn hdds_qos_get_history_depth(qos: *const HddsQoS) -> u32 {
401 if qos.is_null() {
402 return 0;
403 }
404
405 let qos_ref = &*qos.cast::<QoS>();
406 match qos_ref.history {
407 hdds::dds::qos::History::KeepLast(depth) => depth,
408 hdds::dds::qos::History::KeepAll => 0,
409 }
410}
411
412#[no_mangle]
419pub unsafe extern "C" fn hdds_qos_get_deadline_ns(qos: *const HddsQoS) -> u64 {
420 if qos.is_null() {
421 return u64::MAX;
422 }
423
424 let qos_ref = &*qos.cast::<QoS>();
425 qos_ref.deadline.period.as_nanos() as u64
426}
427
428#[no_mangle]
435pub unsafe extern "C" fn hdds_qos_get_lifespan_ns(qos: *const HddsQoS) -> u64 {
436 if qos.is_null() {
437 return u64::MAX;
438 }
439
440 let qos_ref = &*qos.cast::<QoS>();
441 qos_ref.lifespan.duration.as_nanos() as u64
442}
443
444#[no_mangle]
449pub unsafe extern "C" fn hdds_qos_is_ownership_exclusive(qos: *const HddsQoS) -> bool {
450 if qos.is_null() {
451 return false;
452 }
453
454 let qos_ref = &*qos.cast::<QoS>();
455 matches!(
456 qos_ref.ownership.kind,
457 hdds::dds::qos::OwnershipKind::Exclusive
458 )
459}
460
461#[no_mangle]
466pub unsafe extern "C" fn hdds_qos_get_ownership_strength(qos: *const HddsQoS) -> i32 {
467 if qos.is_null() {
468 return 0;
469 }
470
471 let qos_ref = &*qos.cast::<QoS>();
472 qos_ref.ownership_strength.value
473}
474
475#[repr(C)]
477#[derive(Debug, Clone, Copy, PartialEq, Eq)]
478#[allow(clippy::enum_variant_names)] pub enum HddsLivelinessKind {
480 HddsLivelinessAutomatic = 0,
482 HddsLivelinessManualByParticipant = 1,
484 HddsLivelinessManualByTopic = 2,
486}
487
488#[no_mangle]
493pub unsafe extern "C" fn hdds_qos_get_liveliness_kind(qos: *const HddsQoS) -> HddsLivelinessKind {
494 if qos.is_null() {
495 return HddsLivelinessKind::HddsLivelinessAutomatic;
496 }
497
498 let qos_ref = &*qos.cast::<QoS>();
499 match qos_ref.liveliness.kind {
500 hdds::dds::qos::LivelinessKind::Automatic => HddsLivelinessKind::HddsLivelinessAutomatic,
501 hdds::dds::qos::LivelinessKind::ManualByParticipant => {
502 HddsLivelinessKind::HddsLivelinessManualByParticipant
503 }
504 hdds::dds::qos::LivelinessKind::ManualByTopic => {
505 HddsLivelinessKind::HddsLivelinessManualByTopic
506 }
507 }
508}
509
510#[no_mangle]
517pub unsafe extern "C" fn hdds_qos_get_liveliness_lease_ns(qos: *const HddsQoS) -> u64 {
518 if qos.is_null() {
519 return u64::MAX;
520 }
521
522 let qos_ref = &*qos.cast::<QoS>();
523 qos_ref.liveliness.lease_duration.as_nanos() as u64
524}
525
526#[no_mangle]
533pub unsafe extern "C" fn hdds_qos_get_time_based_filter_ns(qos: *const HddsQoS) -> u64 {
534 if qos.is_null() {
535 return 0;
536 }
537
538 let qos_ref = &*qos.cast::<QoS>();
539 qos_ref.time_based_filter.minimum_separation.as_nanos() as u64
540}
541
542#[no_mangle]
549pub unsafe extern "C" fn hdds_qos_get_latency_budget_ns(qos: *const HddsQoS) -> u64 {
550 if qos.is_null() {
551 return 0;
552 }
553
554 let qos_ref = &*qos.cast::<QoS>();
555 qos_ref.latency_budget.duration.as_nanos() as u64
556}
557
558#[no_mangle]
563pub unsafe extern "C" fn hdds_qos_get_transport_priority(qos: *const HddsQoS) -> i32 {
564 if qos.is_null() {
565 return 0;
566 }
567
568 let qos_ref = &*qos.cast::<QoS>();
569 qos_ref.transport_priority.value
570}
571
572#[no_mangle]
579pub unsafe extern "C" fn hdds_qos_get_max_samples(qos: *const HddsQoS) -> usize {
580 if qos.is_null() {
581 return usize::MAX;
582 }
583
584 let qos_ref = &*qos.cast::<QoS>();
585 qos_ref.resource_limits.max_samples
586}
587
588#[no_mangle]
595pub unsafe extern "C" fn hdds_qos_get_max_instances(qos: *const HddsQoS) -> usize {
596 if qos.is_null() {
597 return usize::MAX;
598 }
599
600 let qos_ref = &*qos.cast::<QoS>();
601 qos_ref.resource_limits.max_instances
602}
603
604#[no_mangle]
611pub unsafe extern "C" fn hdds_qos_get_max_samples_per_instance(qos: *const HddsQoS) -> usize {
612 if qos.is_null() {
613 return usize::MAX;
614 }
615
616 let qos_ref = &*qos.cast::<QoS>();
617 qos_ref.resource_limits.max_samples_per_instance
618}
619
620#[no_mangle]
629pub unsafe extern "C" fn hdds_qos_set_liveliness_automatic_ns(
630 qos: *mut HddsQoS,
631 lease_ns: u64,
632) -> HddsError {
633 if qos.is_null() {
634 return HddsError::HddsInvalidArgument;
635 }
636
637 let qos_ref = &mut *qos.cast::<QoS>();
638 qos_ref.liveliness =
639 hdds::dds::qos::Liveliness::automatic(std::time::Duration::from_nanos(lease_ns));
640 HddsError::HddsOk
641}
642
643#[no_mangle]
648pub unsafe extern "C" fn hdds_qos_set_liveliness_manual_participant_ns(
649 qos: *mut HddsQoS,
650 lease_ns: u64,
651) -> HddsError {
652 if qos.is_null() {
653 return HddsError::HddsInvalidArgument;
654 }
655
656 let qos_ref = &mut *qos.cast::<QoS>();
657 qos_ref.liveliness = hdds::dds::qos::Liveliness::manual_by_participant(
658 std::time::Duration::from_nanos(lease_ns),
659 );
660 HddsError::HddsOk
661}
662
663#[no_mangle]
668pub unsafe extern "C" fn hdds_qos_set_liveliness_manual_topic_ns(
669 qos: *mut HddsQoS,
670 lease_ns: u64,
671) -> HddsError {
672 if qos.is_null() {
673 return HddsError::HddsInvalidArgument;
674 }
675
676 let qos_ref = &mut *qos.cast::<QoS>();
677 qos_ref.liveliness =
678 hdds::dds::qos::Liveliness::manual_by_topic(std::time::Duration::from_nanos(lease_ns));
679 HddsError::HddsOk
680}
681
682#[no_mangle]
687pub unsafe extern "C" fn hdds_qos_set_time_based_filter_ns(
688 qos: *mut HddsQoS,
689 min_separation_ns: u64,
690) -> HddsError {
691 if qos.is_null() {
692 return HddsError::HddsInvalidArgument;
693 }
694
695 let qos_ref = &mut *qos.cast::<QoS>();
696 qos_ref.time_based_filter =
697 hdds::dds::qos::TimeBasedFilter::new(std::time::Duration::from_nanos(min_separation_ns));
698 HddsError::HddsOk
699}
700
701#[no_mangle]
706pub unsafe extern "C" fn hdds_qos_set_latency_budget_ns(
707 qos: *mut HddsQoS,
708 budget_ns: u64,
709) -> HddsError {
710 if qos.is_null() {
711 return HddsError::HddsInvalidArgument;
712 }
713
714 let qos_ref = &mut *qos.cast::<QoS>();
715 qos_ref.latency_budget =
716 hdds::dds::qos::LatencyBudget::new(std::time::Duration::from_nanos(budget_ns));
717 HddsError::HddsOk
718}
719
720#[no_mangle]
725pub unsafe extern "C" fn hdds_qos_set_transport_priority(
726 qos: *mut HddsQoS,
727 priority: i32,
728) -> HddsError {
729 if qos.is_null() {
730 return HddsError::HddsInvalidArgument;
731 }
732
733 let qos_ref = &mut *qos.cast::<QoS>();
734 qos_ref.transport_priority.value = priority;
735 HddsError::HddsOk
736}
737
738#[no_mangle]
745pub unsafe extern "C" fn hdds_qos_set_resource_limits(
746 qos: *mut HddsQoS,
747 max_samples: usize,
748 max_instances: usize,
749 max_samples_per_instance: usize,
750) -> HddsError {
751 if qos.is_null() {
752 return HddsError::HddsInvalidArgument;
753 }
754
755 let qos_ref = &mut *qos.cast::<QoS>();
756 qos_ref.resource_limits.max_samples = max_samples;
757 qos_ref.resource_limits.max_instances = max_instances;
758 qos_ref.resource_limits.max_samples_per_instance = max_samples_per_instance;
759 HddsError::HddsOk
760}
761
762#[no_mangle]
772pub unsafe extern "C" fn hdds_qos_clone(qos: *const HddsQoS) -> *mut HddsQoS {
773 if qos.is_null() {
774 return ptr::null_mut();
775 }
776
777 let qos_ref = &*qos.cast::<QoS>();
778 let cloned = Box::new(qos_ref.clone());
779 Box::into_raw(cloned).cast::<HddsQoS>()
780}
781
782#[cfg(test)]
783mod tests {
784 use super::*;
785 use std::ffi::CString;
786
787 #[test]
788 fn test_qos_creation_and_destroy() {
789 unsafe {
790 let qos = hdds_qos_default();
791 assert!(!qos.is_null());
792 hdds_qos_destroy(qos);
793
794 let qos = hdds_qos_reliable();
795 assert!(!qos.is_null());
796 assert!(hdds_qos_is_reliable(qos));
797 hdds_qos_destroy(qos);
798
799 let qos = hdds_qos_best_effort();
800 assert!(!qos.is_null());
801 assert!(!hdds_qos_is_reliable(qos));
802 hdds_qos_destroy(qos);
803 }
804 }
805
806 #[test]
807 fn test_qos_setters() {
808 unsafe {
809 let qos = hdds_qos_default();
810
811 assert_eq!(hdds_qos_set_history_depth(qos, 50), HddsError::HddsOk);
813 assert_eq!(hdds_qos_get_history_depth(qos), 50);
814
815 assert_eq!(hdds_qos_set_transient_local(qos), HddsError::HddsOk);
817 assert!(hdds_qos_is_transient_local(qos));
818
819 assert_eq!(hdds_qos_set_reliable(qos), HddsError::HddsOk);
821 assert!(hdds_qos_is_reliable(qos));
822
823 let part = CString::new("test_partition").unwrap();
825 assert_eq!(
826 hdds_qos_add_partition(qos, part.as_ptr()),
827 HddsError::HddsOk
828 );
829
830 hdds_qos_destroy(qos);
831 }
832 }
833
834 #[test]
835 fn test_qos_clone() {
836 unsafe {
837 let qos = hdds_qos_reliable();
838 hdds_qos_set_history_depth(qos, 42);
839 hdds_qos_set_transient_local(qos);
840
841 let cloned = hdds_qos_clone(qos);
842 assert!(!cloned.is_null());
843 assert!(hdds_qos_is_reliable(cloned));
844 assert!(hdds_qos_is_transient_local(cloned));
845 assert_eq!(hdds_qos_get_history_depth(cloned), 42);
846
847 hdds_qos_destroy(qos);
848 hdds_qos_destroy(cloned);
849 }
850 }
851}