Skip to main content

zerodds_xml/
conformance.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! Conformance-Marker fuer DDS-XML 1.0 §2.4 + §7.2.0.
4//!
5//! § 2.4 — *Atomic Building-Block-Selection.* Spec verlangt, dass die
6//! Implementierung pro Building-Block atomisch sagen kann: "selected"
7//! oder "not selected". Wir markieren das via
8//! [`SUPPORTED_BUILDING_BLOCKS`] — die Liste der vom Crate
9//! produktiv unterstuetzten Building-Blocks. Eine `assert!`-basierte
10//! Test-Tabelle (`tests::supported_blocks_match_repo`) verifiziert,
11//! dass die Liste mit den tatsaechlich exponierten Modulen
12//! uebereinstimmt.
13//!
14//! § 7.2.0 — *1-zu-1-Mapping IDL-Datentypen.* Die Spec sagt: "The XML
15//! representation of resources that correspond to data-types defined
16//! in the DDS IDL PSM is obtained by performing a 1-to-1 mapping of
17//! the corresponding IDL data type." Wir kodieren diese Mapping-
18//! Tabelle als [`IDL_TO_XML_MAPPING`] — pro IDL-Datentyp-Kategorie
19//! ein Verweis auf die produzierende Funktion bzw. das Modul, sodass
20//! Reviewer/Tester die Vollstaendigkeit der Abdeckung an einem Ort
21//! sehen koennen.
22
23extern crate alloc;
24
25/// Liste der Building Blocks aus DDS-XML 1.0 §7.3.1.1, die in diesem
26/// Crate produktiv unterstuetzt sind.
27///
28/// Spec §7.3.1.1: "This specification breaks the syntax used to
29/// represent DDS resources in XML into the six different building
30/// blocks: Building Block QoS, Types, Domains, DomainParticipants,
31/// Applications, Data Samples."
32///
33/// Pro Eintrag: `(spec_name, modul_name, top_level_element)`.
34pub const SUPPORTED_BUILDING_BLOCKS: &[(&str, &str, &str)] = &[
35    ("QoS", "qos", "qos_library"),
36    ("Types", "xtypes_def", "types"),
37    ("Domains", "domain", "domain_library"),
38    (
39        "DomainParticipants",
40        "participant",
41        "domain_participant_library",
42    ),
43    ("Applications", "application", "application_library"),
44    ("DataSamples", "sample", "data"),
45];
46
47/// 1-zu-1-Mapping IDL-Datentyp -> XML-Konstruktor + produzierende
48/// API-Funktion. Spec §7.2.0.
49///
50/// Pro Eintrag: `(idl_kategorie, spec_section, repo_pfad)`.
51pub const IDL_TO_XML_MAPPING: &[(&str, &str, &str)] = &[
52    (
53        "boolean",
54        "§7.1.4 Tab.7.1",
55        "types::parse_bool / parse_bool_strict",
56    ),
57    (
58        "long (32-bit signed)",
59        "§7.1.4 Tab.7.1",
60        "types::parse_long",
61    ),
62    (
63        "unsigned long (32-bit)",
64        "§7.1.4 Tab.7.1",
65        "types::parse_ulong",
66    ),
67    ("string", "§7.1.4 Tab.7.1", "types::parse_string"),
68    ("enum", "§7.1.4 Tab.7.1 / §7.2.1", "types::parse_enum"),
69    ("LENGTH_UNLIMITED", "§7.2.2.1", "types::LENGTH_UNLIMITED"),
70    (
71        "DURATION_INFINITE_SEC/NSEC",
72        "§7.2.2.2 / §7.2.2.3",
73        "types::DURATION_INFINITE_SEC / DURATION_INFINITE_NSEC",
74    ),
75    (
76        "DURATION_ZERO_SEC/NSEC",
77        "§7.2.2.4 / §7.2.2.5",
78        "types::DURATION_ZERO_SEC / DURATION_ZERO_NSEC",
79    ),
80    (
81        "nonNegativeInteger_UNLIMITED",
82        "§7.2.2.8",
83        "types::parse_long (Number-or-Symbol)",
84    ),
85    (
86        "positiveInteger_UNLIMITED",
87        "§7.2.2.9",
88        "types::parse_positive_long_unlimited",
89    ),
90    (
91        "nonNegativeInteger_Duration_SEC",
92        "§7.2.2.10",
93        "types::parse_duration_sec",
94    ),
95    (
96        "nonNegativeInteger_Duration_NSEC",
97        "§7.2.2.11",
98        "types::parse_duration_nsec",
99    ),
100    ("struct (IDL)", "§7.2.3", "qos_parser::* (rekursiv)"),
101    (
102        "sequence<T> (IDL)",
103        "§7.2.4.1",
104        "parser::XmlElement::sequence_elements",
105    ),
106    (
107        "sequence<octet> (IDL)",
108        "§7.2.4.2",
109        "types::parse_octet_sequence + qos_parser::base64_decode",
110    ),
111    (
112        "T[N] (IDL Array)",
113        "§7.2.5",
114        "parser::XmlElement::sequence_elements (re-use)",
115    ),
116    ("Duration_t", "§7.2.6", "qos_parser::parse_duration"),
117];
118
119#[cfg(test)]
120#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
121mod tests {
122    use super::*;
123    use alloc::collections::BTreeSet;
124
125    #[test]
126    fn supported_blocks_match_spec_count() {
127        // Spec §7.3.1.1 nennt **6** Building-Blocks — exakt das ist
128        // unsere Liste.
129        assert_eq!(SUPPORTED_BUILDING_BLOCKS.len(), 6);
130    }
131
132    #[test]
133    fn supported_blocks_have_unique_modules() {
134        let mut seen = BTreeSet::new();
135        for (name, module, _root) in SUPPORTED_BUILDING_BLOCKS {
136            assert!(
137                seen.insert(*module),
138                "Modul `{module}` doppelt fuer Block `{name}` registriert"
139            );
140        }
141    }
142
143    #[test]
144    fn supported_blocks_have_unique_root_elements() {
145        let mut seen = BTreeSet::new();
146        for (name, _module, root) in SUPPORTED_BUILDING_BLOCKS {
147            assert!(
148                seen.insert(*root),
149                "Top-Level-Element `{root}` doppelt fuer Block `{name}`"
150            );
151        }
152    }
153
154    #[test]
155    fn idl_mapping_covers_required_categories() {
156        // Sanity: Mapping-Tabelle enthaelt mindestens alle Kategorien
157        // aus §7.1.4 Tab.7.1 (boolean, enum, long, ulong, string) +
158        // §7.2.x (Sequenzen, Arrays, Duration).
159        let names: BTreeSet<&str> = IDL_TO_XML_MAPPING
160            .iter()
161            .map(|(name, _, _)| *name)
162            .collect();
163        for required in [
164            "boolean",
165            "long (32-bit signed)",
166            "unsigned long (32-bit)",
167            "string",
168            "enum",
169            "Duration_t",
170        ] {
171            assert!(
172                names.contains(required),
173                "Mapping-Tabelle fehlt Eintrag fuer `{required}`"
174            );
175        }
176    }
177
178    #[test]
179    fn idl_mapping_entries_unique() {
180        let mut seen = BTreeSet::new();
181        for (name, _, _) in IDL_TO_XML_MAPPING {
182            assert!(seen.insert(*name), "Mapping-Eintrag `{name}` doppelt");
183        }
184    }
185
186    #[test]
187    fn idl_mapping_includes_section_7_2_x_items() {
188        // §7.2.x-Items, die nach K7-A vollstaendig live sind, muessen
189        // in der Tabelle stehen — sonst kann §7.2.0 nicht "done" sein.
190        let sections: BTreeSet<&str> = IDL_TO_XML_MAPPING.iter().map(|(_, sec, _)| *sec).collect();
191        for required in ["§7.2.2.9", "§7.2.4.1", "§7.2.4.2", "§7.2.5", "§7.2.6"] {
192            assert!(
193                sections.contains(required),
194                "Mapping-Tabelle fehlt §-Sektion `{required}`"
195            );
196        }
197    }
198}