1use core::fmt::Write;
20
21use crate::error::CppGenError;
22
23#[derive(Debug, Clone, Copy)]
25struct QosField {
26 name: &'static str,
28 cpp_ty: &'static str,
30 default_init: &'static str,
32}
33
34#[derive(Debug, Clone, Copy)]
36struct QosSpec {
37 name: &'static str,
39 fields: &'static [QosField],
41 spec_ref: &'static str,
43}
44
45#[allow(clippy::too_many_lines)]
48const POLICIES: &[QosSpec] = &[
49 QosSpec {
50 name: "UserDataQosPolicy",
51 spec_ref: "§2.2.3.1",
52 fields: &[QosField {
53 name: "value",
54 cpp_ty: "std::vector<uint8_t>",
55 default_init: "{}",
56 }],
57 },
58 QosSpec {
59 name: "TopicDataQosPolicy",
60 spec_ref: "§2.2.3.2",
61 fields: &[QosField {
62 name: "value",
63 cpp_ty: "std::vector<uint8_t>",
64 default_init: "{}",
65 }],
66 },
67 QosSpec {
68 name: "GroupDataQosPolicy",
69 spec_ref: "§2.2.3.3",
70 fields: &[QosField {
71 name: "value",
72 cpp_ty: "std::vector<uint8_t>",
73 default_init: "{}",
74 }],
75 },
76 QosSpec {
77 name: "DurabilityQosPolicy",
78 spec_ref: "§2.2.3.4",
79 fields: &[QosField {
80 name: "kind",
81 cpp_ty: "::dds::core::policy::DurabilityKind",
82 default_init: "::dds::core::policy::DurabilityKind::Volatile",
83 }],
84 },
85 QosSpec {
86 name: "DurabilityServiceQosPolicy",
87 spec_ref: "§2.2.3.5",
88 fields: &[
89 QosField {
90 name: "service_cleanup_delay",
91 cpp_ty: "::dds::core::Duration",
92 default_init: "::dds::core::Duration::zero()",
93 },
94 QosField {
95 name: "history_kind",
96 cpp_ty: "::dds::core::policy::HistoryKind",
97 default_init: "::dds::core::policy::HistoryKind::KeepLast",
98 },
99 QosField {
100 name: "history_depth",
101 cpp_ty: "int32_t",
102 default_init: "1",
103 },
104 QosField {
105 name: "max_samples",
106 cpp_ty: "int32_t",
107 default_init: "-1",
108 },
109 QosField {
110 name: "max_instances",
111 cpp_ty: "int32_t",
112 default_init: "-1",
113 },
114 QosField {
115 name: "max_samples_per_instance",
116 cpp_ty: "int32_t",
117 default_init: "-1",
118 },
119 ],
120 },
121 QosSpec {
122 name: "PresentationQosPolicy",
123 spec_ref: "§2.2.3.6",
124 fields: &[
125 QosField {
126 name: "access_scope",
127 cpp_ty: "::dds::core::policy::PresentationAccessScopeKind",
128 default_init: "::dds::core::policy::PresentationAccessScopeKind::Instance",
129 },
130 QosField {
131 name: "coherent_access",
132 cpp_ty: "bool",
133 default_init: "false",
134 },
135 QosField {
136 name: "ordered_access",
137 cpp_ty: "bool",
138 default_init: "false",
139 },
140 ],
141 },
142 QosSpec {
143 name: "DeadlineQosPolicy",
144 spec_ref: "§2.2.3.7",
145 fields: &[QosField {
146 name: "period",
147 cpp_ty: "::dds::core::Duration",
148 default_init: "::dds::core::Duration::infinite()",
149 }],
150 },
151 QosSpec {
152 name: "LatencyBudgetQosPolicy",
153 spec_ref: "§2.2.3.8",
154 fields: &[QosField {
155 name: "duration",
156 cpp_ty: "::dds::core::Duration",
157 default_init: "::dds::core::Duration::zero()",
158 }],
159 },
160 QosSpec {
161 name: "OwnershipQosPolicy",
162 spec_ref: "§2.2.3.9",
163 fields: &[QosField {
164 name: "kind",
165 cpp_ty: "::dds::core::policy::OwnershipKind",
166 default_init: "::dds::core::policy::OwnershipKind::Shared",
167 }],
168 },
169 QosSpec {
170 name: "OwnershipStrengthQosPolicy",
171 spec_ref: "§2.2.3.10",
172 fields: &[QosField {
173 name: "value",
174 cpp_ty: "int32_t",
175 default_init: "0",
176 }],
177 },
178 QosSpec {
179 name: "LivelinessQosPolicy",
180 spec_ref: "§2.2.3.11",
181 fields: &[
182 QosField {
183 name: "kind",
184 cpp_ty: "::dds::core::policy::LivelinessKind",
185 default_init: "::dds::core::policy::LivelinessKind::Automatic",
186 },
187 QosField {
188 name: "lease_duration",
189 cpp_ty: "::dds::core::Duration",
190 default_init: "::dds::core::Duration::infinite()",
191 },
192 ],
193 },
194 QosSpec {
195 name: "TimeBasedFilterQosPolicy",
196 spec_ref: "§2.2.3.12",
197 fields: &[QosField {
198 name: "minimum_separation",
199 cpp_ty: "::dds::core::Duration",
200 default_init: "::dds::core::Duration::zero()",
201 }],
202 },
203 QosSpec {
204 name: "PartitionQosPolicy",
205 spec_ref: "§2.2.3.13",
206 fields: &[QosField {
207 name: "name",
208 cpp_ty: "std::vector<std::string>",
209 default_init: "{}",
210 }],
211 },
212 QosSpec {
213 name: "ReliabilityQosPolicy",
214 spec_ref: "§2.2.3.14",
215 fields: &[
216 QosField {
217 name: "kind",
218 cpp_ty: "::dds::core::policy::ReliabilityKind",
219 default_init: "::dds::core::policy::ReliabilityKind::BestEffort",
220 },
221 QosField {
222 name: "max_blocking_time",
223 cpp_ty: "::dds::core::Duration",
224 default_init: "::dds::core::Duration::from_millis(100)",
225 },
226 ],
227 },
228 QosSpec {
229 name: "TransportPriorityQosPolicy",
230 spec_ref: "§2.2.3.15",
231 fields: &[QosField {
232 name: "value",
233 cpp_ty: "int32_t",
234 default_init: "0",
235 }],
236 },
237 QosSpec {
238 name: "LifespanQosPolicy",
239 spec_ref: "§2.2.3.16",
240 fields: &[QosField {
241 name: "duration",
242 cpp_ty: "::dds::core::Duration",
243 default_init: "::dds::core::Duration::infinite()",
244 }],
245 },
246 QosSpec {
247 name: "DestinationOrderQosPolicy",
248 spec_ref: "§2.2.3.17",
249 fields: &[QosField {
250 name: "kind",
251 cpp_ty: "::dds::core::policy::DestinationOrderKind",
252 default_init: "::dds::core::policy::DestinationOrderKind::ByReceptionTimestamp",
253 }],
254 },
255 QosSpec {
256 name: "HistoryQosPolicy",
257 spec_ref: "§2.2.3.18",
258 fields: &[
259 QosField {
260 name: "kind",
261 cpp_ty: "::dds::core::policy::HistoryKind",
262 default_init: "::dds::core::policy::HistoryKind::KeepLast",
263 },
264 QosField {
265 name: "depth",
266 cpp_ty: "int32_t",
267 default_init: "1",
268 },
269 ],
270 },
271 QosSpec {
272 name: "ResourceLimitsQosPolicy",
273 spec_ref: "§2.2.3.19",
274 fields: &[
275 QosField {
276 name: "max_samples",
277 cpp_ty: "int32_t",
278 default_init: "-1",
279 },
280 QosField {
281 name: "max_instances",
282 cpp_ty: "int32_t",
283 default_init: "-1",
284 },
285 QosField {
286 name: "max_samples_per_instance",
287 cpp_ty: "int32_t",
288 default_init: "-1",
289 },
290 ],
291 },
292 QosSpec {
293 name: "EntityFactoryQosPolicy",
294 spec_ref: "§2.2.3.20",
295 fields: &[QosField {
296 name: "autoenable_created_entities",
297 cpp_ty: "bool",
298 default_init: "true",
299 }],
300 },
301 QosSpec {
302 name: "WriterDataLifecycleQosPolicy",
303 spec_ref: "§2.2.3.21",
304 fields: &[QosField {
305 name: "autodispose_unregistered_instances",
306 cpp_ty: "bool",
307 default_init: "true",
308 }],
309 },
310 QosSpec {
311 name: "ReaderDataLifecycleQosPolicy",
312 spec_ref: "§2.2.3.22",
313 fields: &[
314 QosField {
315 name: "autopurge_nowriter_samples_delay",
316 cpp_ty: "::dds::core::Duration",
317 default_init: "::dds::core::Duration::infinite()",
318 },
319 QosField {
320 name: "autopurge_disposed_samples_delay",
321 cpp_ty: "::dds::core::Duration",
322 default_init: "::dds::core::Duration::infinite()",
323 },
324 ],
325 },
326];
327
328pub fn emit_qos_header(out: &mut String) -> Result<(), CppGenError> {
334 writeln!(out, "// Block-G: DDS-QoS-Policies (Spec dds-1.4 §2.2.3).").map_err(fmt_err)?;
335 writeln!(
336 out,
337 "namespace dds {{ namespace core {{ namespace policy {{"
338 )
339 .map_err(fmt_err)?;
340 writeln!(out).map_err(fmt_err)?;
341
342 writeln!(
345 out,
346 "// Forward-Declarations der Kind-Enums (siehe DDS 1.4 §2.2.3)."
347 )
348 .map_err(fmt_err)?;
349 for kind in [
350 "DurabilityKind",
351 "PresentationAccessScopeKind",
352 "OwnershipKind",
353 "LivelinessKind",
354 "ReliabilityKind",
355 "DestinationOrderKind",
356 "HistoryKind",
357 ] {
358 writeln!(out, "enum class {kind} : int32_t;").map_err(fmt_err)?;
359 }
360 writeln!(out, "using QosPolicyId = int32_t;").map_err(fmt_err)?;
361 writeln!(out).map_err(fmt_err)?;
362
363 for p in POLICIES {
364 emit_policy_class(out, p)?;
365 }
366
367 writeln!(out, "}} }} }} // namespace dds::core::policy").map_err(fmt_err)?;
368 writeln!(out).map_err(fmt_err)?;
369
370 emit_type_traits(out)?;
371
372 Ok(())
373}
374
375#[must_use]
377pub fn policy_class_names() -> Vec<&'static str> {
378 POLICIES.iter().map(|p| p.name).collect()
379}
380
381fn emit_policy_class(out: &mut String, p: &QosSpec) -> Result<(), CppGenError> {
382 writeln!(out, "/// {} ({})", p.name, p.spec_ref).map_err(fmt_err)?;
383 writeln!(out, "class {} {{", p.name).map_err(fmt_err)?;
384 writeln!(out, "public:").map_err(fmt_err)?;
385 writeln!(out, " {}() = default;", p.name).map_err(fmt_err)?;
386 writeln!(out, " ~{}() = default;", p.name).map_err(fmt_err)?;
387 writeln!(out, " {0}(const {0}&) = default;", p.name).map_err(fmt_err)?;
388 writeln!(out, " {0}({0}&&) noexcept = default;", p.name).map_err(fmt_err)?;
389 writeln!(out, " {0}& operator=(const {0}&) = default;", p.name).map_err(fmt_err)?;
390 writeln!(out, " {0}& operator=({0}&&) noexcept = default;", p.name).map_err(fmt_err)?;
391 writeln!(out).map_err(fmt_err)?;
392
393 for f in p.fields {
394 writeln!(
396 out,
397 " const {ty}& {name}() const {{ return {name}_; }}",
398 ty = f.cpp_ty,
399 name = f.name
400 )
401 .map_err(fmt_err)?;
402 writeln!(
404 out,
405 " {ty}& {name}() {{ return {name}_; }}",
406 ty = f.cpp_ty,
407 name = f.name
408 )
409 .map_err(fmt_err)?;
410 writeln!(
412 out,
413 " void {name}(const {ty}& value) {{ {name}_ = value; }}",
414 ty = f.cpp_ty,
415 name = f.name
416 )
417 .map_err(fmt_err)?;
418 writeln!(
420 out,
421 " void {name}({ty}&& value) {{ {name}_ = std::move(value); }}",
422 ty = f.cpp_ty,
423 name = f.name
424 )
425 .map_err(fmt_err)?;
426 }
427
428 if !p.fields.is_empty() {
429 writeln!(out).map_err(fmt_err)?;
430 write!(
432 out,
433 " bool operator==(const {}& rhs) const {{ return ",
434 p.name
435 )
436 .map_err(fmt_err)?;
437 for (i, f) in p.fields.iter().enumerate() {
438 if i > 0 {
439 write!(out, " && ").map_err(fmt_err)?;
440 }
441 write!(out, "{name}_ == rhs.{name}_", name = f.name).map_err(fmt_err)?;
442 }
443 writeln!(out, "; }}").map_err(fmt_err)?;
444 writeln!(
445 out,
446 " bool operator!=(const {0}& rhs) const {{ return !(*this == rhs); }}",
447 p.name
448 )
449 .map_err(fmt_err)?;
450 }
451
452 if !p.fields.is_empty() {
453 writeln!(out).map_err(fmt_err)?;
454 writeln!(out, "private:").map_err(fmt_err)?;
455 for f in p.fields {
456 writeln!(
457 out,
458 " {ty} {name}_{{{init}}};",
459 ty = f.cpp_ty,
460 name = f.name,
461 init = f.default_init
462 )
463 .map_err(fmt_err)?;
464 }
465 }
466
467 writeln!(out, "}};").map_err(fmt_err)?;
468 writeln!(out).map_err(fmt_err)?;
469 Ok(())
470}
471
472fn emit_type_traits(out: &mut String) -> Result<(), CppGenError> {
479 writeln!(out, "// idl4-cpp §7.1.4 Tab.7.1 — Argument-Type-Traits.").map_err(fmt_err)?;
480 writeln!(
481 out,
482 "namespace dds {{ namespace core {{ namespace traits {{"
483 )
484 .map_err(fmt_err)?;
485 writeln!(out).map_err(fmt_err)?;
486 writeln!(out, "template <typename T>").map_err(fmt_err)?;
487 writeln!(out, "struct value_type {{ using type = T; }};").map_err(fmt_err)?;
488 writeln!(out).map_err(fmt_err)?;
489 writeln!(out, "template <typename T>").map_err(fmt_err)?;
490 writeln!(out, "struct in_type {{ using type = const T&; }};").map_err(fmt_err)?;
491 writeln!(out).map_err(fmt_err)?;
492 writeln!(out, "template <typename T>").map_err(fmt_err)?;
493 writeln!(out, "struct out_type {{ using type = T&; }};").map_err(fmt_err)?;
494 writeln!(out).map_err(fmt_err)?;
495 writeln!(out, "template <typename T>").map_err(fmt_err)?;
496 writeln!(out, "struct inout_type {{ using type = T&; }};").map_err(fmt_err)?;
497 writeln!(out).map_err(fmt_err)?;
498 writeln!(out, "template <typename T>").map_err(fmt_err)?;
500 writeln!(out, "using value_type_t = typename value_type<T>::type;").map_err(fmt_err)?;
501 writeln!(out, "template <typename T>").map_err(fmt_err)?;
502 writeln!(out, "using in_type_t = typename in_type<T>::type;").map_err(fmt_err)?;
503 writeln!(out, "template <typename T>").map_err(fmt_err)?;
504 writeln!(out, "using out_type_t = typename out_type<T>::type;").map_err(fmt_err)?;
505 writeln!(out, "template <typename T>").map_err(fmt_err)?;
506 writeln!(out, "using inout_type_t = typename inout_type<T>::type;").map_err(fmt_err)?;
507 writeln!(out).map_err(fmt_err)?;
508 writeln!(out, "}} }} }} // namespace dds::core::traits").map_err(fmt_err)?;
509 writeln!(out).map_err(fmt_err)?;
510 Ok(())
511}
512
513fn fmt_err(_: core::fmt::Error) -> CppGenError {
514 CppGenError::Internal("string formatting failed".into())
515}
516
517#[cfg(test)]
518mod tests {
519 #![allow(clippy::expect_used, clippy::panic)]
520 use super::*;
521
522 fn render() -> String {
523 let mut s = String::new();
524 emit_qos_header(&mut s).expect("emit");
525 s
526 }
527
528 #[test]
529 fn qos_namespace_is_dds_core_policy() {
530 let s = render();
531 assert!(s.contains("namespace dds { namespace core { namespace policy {"));
532 assert!(s.contains("} } } // namespace dds::core::policy"));
533 }
534
535 #[test]
536 fn reliability_qos_policy_has_kind_and_max_blocking_time() {
537 let s = render();
538 assert!(s.contains("class ReliabilityQosPolicy {"));
539 assert!(s.contains("ReliabilityKind kind_"));
540 assert!(s.contains("::dds::core::Duration max_blocking_time_"));
541 }
542
543 #[test]
544 fn move_setter_uses_rvalue_and_std_move() {
545 let s = render();
546 assert!(s.contains("void max_blocking_time(::dds::core::Duration&& value)"));
547 assert!(s.contains("max_blocking_time_ = std::move(value);"));
548 }
549
550 #[test]
551 fn const_getter_returns_const_ref() {
552 let s = render();
553 assert!(s.contains("const ::dds::core::policy::ReliabilityKind& kind() const"));
554 assert!(s.contains("::dds::core::policy::ReliabilityKind& kind()"));
555 }
556
557 #[test]
558 fn mutable_getter_returns_mutable_ref() {
559 let s = render();
560 let needle = "::dds::core::Duration& max_blocking_time() {";
562 assert!(s.contains(needle));
563 }
564
565 #[test]
566 fn history_qos_policy_has_kind_and_depth() {
567 let s = render();
568 assert!(s.contains("class HistoryQosPolicy {"));
569 assert!(s.contains("HistoryKind kind_"));
570 assert!(s.contains("int32_t depth_{1}"));
571 }
572
573 #[test]
574 fn resource_limits_has_three_fields() {
575 let s = render();
576 assert!(s.contains("class ResourceLimitsQosPolicy {"));
577 assert!(s.contains("int32_t max_samples_{-1}"));
578 assert!(s.contains("int32_t max_instances_{-1}"));
579 assert!(s.contains("int32_t max_samples_per_instance_{-1}"));
580 }
581
582 #[test]
583 fn deadline_qos_policy_default_is_infinite() {
584 let s = render();
585 assert!(s.contains("class DeadlineQosPolicy {"));
586 assert!(s.contains("Duration period_{::dds::core::Duration::infinite()}"));
587 }
588
589 #[test]
590 fn user_data_uses_byte_vector() {
591 let s = render();
592 assert!(s.contains("class UserDataQosPolicy {"));
593 assert!(s.contains("std::vector<uint8_t> value_"));
594 }
595
596 #[test]
597 fn partition_qos_policy_uses_string_vector() {
598 let s = render();
599 assert!(s.contains("class PartitionQosPolicy {"));
600 assert!(s.contains("std::vector<std::string> name_"));
601 }
602
603 #[test]
604 fn all_22_policies_emitted() {
605 let names = policy_class_names();
606 assert_eq!(names.len(), 22);
607 let s = render();
608 for n in names {
609 assert!(s.contains(&format!("class {n} {{")), "missing: {n}");
610 }
611 }
612
613 #[test]
614 fn type_traits_namespace_emitted() {
615 let s = render();
616 assert!(s.contains("namespace dds { namespace core { namespace traits {"));
617 assert!(s.contains("struct value_type"));
618 assert!(s.contains("struct in_type"));
619 assert!(s.contains("struct out_type"));
620 assert!(s.contains("struct inout_type"));
621 }
622
623 #[test]
624 fn type_traits_value_type_is_t_by_value() {
625 let s = render();
626 assert!(s.contains("struct value_type { using type = T; };"));
627 }
628
629 #[test]
630 fn type_traits_in_type_is_const_ref() {
631 let s = render();
632 assert!(s.contains("struct in_type { using type = const T&; };"));
633 }
634
635 #[test]
636 fn type_traits_out_and_inout_are_ref() {
637 let s = render();
638 assert!(s.contains("struct out_type { using type = T&; };"));
639 assert!(s.contains("struct inout_type { using type = T&; };"));
640 }
641
642 #[test]
643 fn type_trait_aliases_with_t_suffix() {
644 let s = render();
645 assert!(s.contains("using value_type_t = typename value_type<T>::type;"));
646 assert!(s.contains("using in_type_t = typename in_type<T>::type;"));
647 assert!(s.contains("using out_type_t = typename out_type<T>::type;"));
648 assert!(s.contains("using inout_type_t = typename inout_type<T>::type;"));
649 }
650
651 #[test]
652 fn equality_operator_compares_fields() {
653 let s = render();
654 assert!(s.contains("kind_ == rhs.kind_ && max_blocking_time_ == rhs.max_blocking_time_"));
656 assert!(s.contains("bool operator!=(const ReliabilityQosPolicy& rhs)"));
657 }
658
659 #[test]
660 fn entity_factory_default_is_true() {
661 let s = render();
662 assert!(s.contains("class EntityFactoryQosPolicy {"));
663 assert!(s.contains("bool autoenable_created_entities_{true}"));
664 }
665
666 #[test]
667 fn ownership_strength_default_is_zero() {
668 let s = render();
669 assert!(s.contains("class OwnershipStrengthQosPolicy {"));
670 assert!(s.contains("int32_t value_{0}"));
671 }
672
673 #[test]
674 fn liveliness_qos_policy_has_kind_and_lease() {
675 let s = render();
676 assert!(s.contains("class LivelinessQosPolicy {"));
677 assert!(s.contains("LivelinessKind kind_"));
678 assert!(s.contains("Duration lease_duration_"));
679 }
680}