Skip to main content

zerodds_idl/
config.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! Konfiguration fuer die Public-`parse()`-API (T5.5).
4//!
5//! [`ParserConfig`] kombiniert die Grammar-Auswahl ([`IdlVersion`]),
6//! den Strenge-Grad ([`CompatMode`]) und Vendor-spezifische Erweiterungen
7//! ([`VendorExt`]). Default ist OMG-konform IDL 4.2.
8//!
9//! Vendor-Extensions sind in Phase 0 leer (Hooks nur). Konkrete
10//! RTI-/OpenSplice-Deltas folgen mit Task 6.4 (`grammar::deltas`).
11
12use crate::features::IdlFeatures;
13use crate::grammar::IdlVersion;
14
15/// Konfiguration fuer den Top-Level-Parser.
16///
17/// # Beispiel
18/// ```
19/// use zerodds_idl::config::ParserConfig;
20/// let cfg = ParserConfig::default();
21/// assert_eq!(cfg.version, zerodds_idl::grammar::IdlVersion::V4_2);
22/// ```
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
24pub struct ParserConfig {
25    /// Welche IDL-Spec-Version geparst werden soll. Default: V4_2.
26    pub version: IdlVersion,
27    /// Wie streng wird das OMG-Grammatik-Regelwerk durchgesetzt.
28    pub compat: CompatMode,
29    /// Welche Vendor-Extension(s) zusaetzlich aktiv sind. Legacy-Hook;
30    /// pro-Konstrukt-Aktivierung steuert jetzt [`IdlFeatures`].
31    pub vendor: VendorExt,
32    /// Building-Block-Profil (§2.3 + §9). Steuert welche Spec-Sektionen
33    /// (DDS-Basic, DDS-Extensible, Full mit CORBA/CCM) als gueltig
34    /// angesehen werden. Default `DdsExtensible` reflektiert den
35    /// ZeroDDS-Use-Case (DDS-Stack mit XTypes).
36    pub profile: Profile,
37    /// Feature-Flag-Maske fuer pro-Konstrukt-Aktivierung. Default
38    /// folgt dem `profile`-Feld; User kann zusaetzlich einzelne Flags
39    /// togglen (z.B. CORBA-Template-Modules in DDS-Extensible-Profil
40    /// fuer DDS-RPC-Service-Templates).
41    pub features: IdlFeatures,
42}
43
44impl Default for ParserConfig {
45    fn default() -> Self {
46        Self {
47            version: IdlVersion::V4_2,
48            compat: CompatMode::Strict,
49            vendor: VendorExt::None,
50            profile: Profile::DdsExtensible,
51            features: IdlFeatures::dds_extensible(),
52        }
53    }
54}
55
56impl ParserConfig {
57    /// Convenience: strikte OMG-IDL-4.2-Konfiguration (== `Default`).
58    #[must_use]
59    pub const fn strict_4_2() -> Self {
60        Self {
61            version: IdlVersion::V4_2,
62            compat: CompatMode::Strict,
63            vendor: VendorExt::None,
64            profile: Profile::DdsExtensible,
65            features: IdlFeatures::dds_extensible(),
66        }
67    }
68
69    /// Convenience: vendor-pragmatische Konfiguration mit lockerer
70    /// Strict-Auslegung (z.B. leere `<member_list>` zugelassen).
71    #[must_use]
72    pub const fn pragmatic_4_2() -> Self {
73        Self {
74            version: IdlVersion::V4_2,
75            compat: CompatMode::Pragmatic,
76            vendor: VendorExt::None,
77            profile: Profile::DdsExtensible,
78            features: IdlFeatures::dds_extensible(),
79        }
80    }
81
82    /// Convenience: Plain-DDS-Profil (§2.3a) — minimal DDS-Stack ohne
83    /// XTypes-Erweiterungen, ohne CORBA/CCM-Konstrukte. Strict.
84    #[must_use]
85    pub const fn plain_dds_4_2() -> Self {
86        Self {
87            version: IdlVersion::V4_2,
88            compat: CompatMode::Strict,
89            vendor: VendorExt::None,
90            profile: Profile::DdsBasic,
91            features: IdlFeatures::dds_basic(),
92        }
93    }
94
95    /// Convenience: Full-Profil — IDL 4.2 mit allen Building-Blocks
96    /// (inkl. CORBA-Profile, falls in der Grammar aktiviert).
97    #[must_use]
98    pub const fn full_4_2() -> Self {
99        Self {
100            version: IdlVersion::V4_2,
101            compat: CompatMode::Strict,
102            vendor: VendorExt::None,
103            profile: Profile::Full,
104            features: IdlFeatures::corba_full(),
105        }
106    }
107
108    /// OpenSplice-Legacy-Migration-Profil: CORBA-Full + Legacy-Pragmas.
109    /// Fuer Referenz-Kunden, die uralte OpenSplice-IDLs upgraden.
110    #[must_use]
111    pub const fn opensplice_legacy() -> Self {
112        Self {
113            version: IdlVersion::V4_2,
114            compat: CompatMode::Pragmatic,
115            vendor: VendorExt::OpenSplice,
116            profile: Profile::Full,
117            features: IdlFeatures::opensplice_legacy(),
118        }
119    }
120
121    /// OpenSplice-Modern-Profil.
122    #[must_use]
123    pub const fn opensplice_modern() -> Self {
124        Self {
125            version: IdlVersion::V4_2,
126            compat: CompatMode::Strict,
127            vendor: VendorExt::OpenSplice,
128            profile: Profile::DdsExtensible,
129            features: IdlFeatures::opensplice_modern(),
130        }
131    }
132
133    /// RTI-Connext-Profil.
134    #[must_use]
135    pub const fn rti_connext() -> Self {
136        Self {
137            version: IdlVersion::V4_2,
138            compat: CompatMode::Strict,
139            vendor: VendorExt::Rti,
140            profile: Profile::DdsExtensible,
141            features: IdlFeatures::rti_connext(),
142        }
143    }
144
145    /// Cyclone-DDS-Profil.
146    #[must_use]
147    pub const fn cyclonedds() -> Self {
148        Self {
149            version: IdlVersion::V4_2,
150            compat: CompatMode::Strict,
151            vendor: VendorExt::None,
152            profile: Profile::DdsExtensible,
153            features: IdlFeatures::cyclonedds(),
154        }
155    }
156
157    /// FastDDS-Profil.
158    #[must_use]
159    pub const fn fastdds() -> Self {
160        Self {
161            version: IdlVersion::V4_2,
162            compat: CompatMode::Strict,
163            vendor: VendorExt::None,
164            profile: Profile::DdsExtensible,
165            features: IdlFeatures::fastdds(),
166        }
167    }
168}
169
170/// Wie streng der Parser die OMG-Spec auslegt.
171///
172/// In Phase 0 hat das Setting noch keinen Verhaltens-Effekt — die Grammar
173/// ist hardgecodet vendor-pragmatisch (leere member_list etc.). Mit T6.x
174/// (`grammar::deltas`) wird `Strict` die Pragmatik-Lockerungen abdrehen.
175#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
176pub enum CompatMode {
177    /// OMG-konform mit allen `+`/`*`-Quantor-Pflichten.
178    #[default]
179    Strict,
180    /// Vendor-pragmatisch: leere Listen wo Spec `+` fordert, weiche
181    /// Disambiguation an Stellen mit ueblichen Vendor-Erweiterungen.
182    Pragmatic,
183}
184
185/// Building-Block-Profil (§2.3 + §9 — IDL 4.2 Conformance).
186///
187/// IDL 4.2 ist modular aus Building-Blocks aufgebaut. Diese Achse
188/// selektiert welche Sektionen aktiv sind:
189///
190/// - **`DdsBasic`** — minimales DDS-Profil: nur `module`/`struct`/`union`/
191///   `enum`/`typedef`/`const`/`exception` plus DDS-Annotations. Kein
192///   `interface`, kein `valuetype`, kein XTypes-Map/Bitset/Bitmask, keine
193///   CORBA/CCM-Konstrukte. Spec-Konformitaets-Ziel fuer schlanke
194///   Topic-Type-IDLs.
195/// - **`DdsExtensible`** — DDS plus XTypes (§7.4.13: map/bitset/bitmask/
196///   any), Annotation-Defs, `interface` fuer DDS-RPC. Default fuer den
197///   ZeroDDS-Use-Case.
198/// - **`Full`** — alle Building-Blocks der IDL 4.2 inkl. (zukuenftiger)
199///   CORBA-Profile (Components/Homes/Template-Modules). Heute identisch
200///   zu `DdsExtensible`, weil CORBA-Konstrukte noch nicht implementiert
201///   sind (siehe `idl-4.2.open.md` Sektion 7).
202///
203/// Profil ist *deklarativ* — die Grammar selbst ist
204/// monolithisch und akzeptiert immer den `DdsExtensible`-Umfang. Der
205/// Profil-Selektor wird mit CORBA-Konstrukten greifen
206/// (selektive Production-Aktivierung im `GrammarComposer`).
207#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
208pub enum Profile {
209    /// Plain-DDS — minimaler DDS-Topic-Type-Umfang.
210    DdsBasic,
211    /// DDS + XTypes + DDS-RPC (Default fuer DDS-Stacks).
212    #[default]
213    DdsExtensible,
214    /// Full IDL 4.2 inkl. (zukuenftiger) CORBA/CCM-Profile.
215    Full,
216}
217
218/// Aktive Vendor-Extension fuer den Parser-Lauf.
219///
220/// Mit der Einfuehrung der Feature-Flag-Achse [`IdlFeatures`] ist dieser
221/// Enum primaer ein UI-/Routing-Hint; die eigentliche Aktivierung der
222/// Vendor-Konstrukte steuert das `features`-Feld.
223#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
224pub enum VendorExt {
225    /// Keine Vendor-Erweiterung. Default.
226    #[default]
227    None,
228    /// RTI Connext DDS.
229    Rti,
230    /// OpenSplice (modern oder legacy — siehe `IdlFeatures::vendor_*`).
231    OpenSplice,
232    /// Cyclone DDS.
233    CycloneDds,
234    /// FastDDS (eProsima).
235    FastDds,
236}
237
238#[cfg(test)]
239mod tests {
240    #![allow(clippy::expect_used)]
241
242    use super::*;
243
244    #[test]
245    fn default_is_strict_4_2_no_vendor() {
246        let cfg = ParserConfig::default();
247        assert_eq!(cfg.version, IdlVersion::V4_2);
248        assert_eq!(cfg.compat, CompatMode::Strict);
249        assert_eq!(cfg.vendor, VendorExt::None);
250    }
251
252    #[test]
253    fn strict_4_2_constructor_matches_default() {
254        assert_eq!(ParserConfig::strict_4_2(), ParserConfig::default());
255    }
256
257    #[test]
258    fn pragmatic_4_2_keeps_version_and_no_vendor() {
259        let cfg = ParserConfig::pragmatic_4_2();
260        assert_eq!(cfg.version, IdlVersion::V4_2);
261        assert_eq!(cfg.compat, CompatMode::Pragmatic);
262        assert_eq!(cfg.vendor, VendorExt::None);
263    }
264
265    #[test]
266    fn config_is_copyable() {
267        let cfg = ParserConfig::default();
268        let copy = cfg;
269        assert_eq!(cfg, copy);
270    }
271
272    #[test]
273    fn compat_mode_default_is_strict() {
274        assert_eq!(CompatMode::default(), CompatMode::Strict);
275    }
276
277    #[test]
278    fn vendor_ext_default_is_none() {
279        assert_eq!(VendorExt::default(), VendorExt::None);
280    }
281
282    #[test]
283    fn config_can_be_customized_field_by_field() {
284        let cfg = ParserConfig {
285            version: IdlVersion::V4_0,
286            compat: CompatMode::Pragmatic,
287            vendor: VendorExt::Rti,
288            profile: Profile::Full,
289            features: IdlFeatures::corba_full(),
290        };
291        assert_eq!(cfg.version, IdlVersion::V4_0);
292        assert_eq!(cfg.compat, CompatMode::Pragmatic);
293        assert_eq!(cfg.vendor, VendorExt::Rti);
294        assert_eq!(cfg.profile, Profile::Full);
295        assert!(cfg.features.corba_components);
296    }
297
298    #[test]
299    fn opensplice_legacy_constructor_enables_corba_and_pragmas() {
300        let cfg = ParserConfig::opensplice_legacy();
301        assert!(cfg.features.vendor_opensplice_legacy);
302        assert!(cfg.features.corba_value_types_full);
303        assert!(cfg.features.corba_components);
304        assert_eq!(cfg.vendor, VendorExt::OpenSplice);
305    }
306
307    #[test]
308    fn rti_connext_constructor() {
309        let cfg = ParserConfig::rti_connext();
310        assert!(cfg.features.vendor_rti);
311        assert_eq!(cfg.vendor, VendorExt::Rti);
312        assert!(!cfg.features.corba_components);
313    }
314
315    #[test]
316    fn cyclonedds_constructor() {
317        let cfg = ParserConfig::cyclonedds();
318        assert!(cfg.features.vendor_cyclonedds);
319        assert!(!cfg.features.vendor_rti);
320    }
321
322    #[test]
323    fn fastdds_constructor() {
324        let cfg = ParserConfig::fastdds();
325        assert!(cfg.features.vendor_fastdds);
326        assert!(!cfg.features.vendor_opensplice);
327    }
328
329    // -----------------------------------------------------------------
330    // §2.3 + §9 — Profile-Selektor (§10 Open-List)
331    // -----------------------------------------------------------------
332
333    #[test]
334    fn default_profile_is_dds_extensible() {
335        assert_eq!(ParserConfig::default().profile, Profile::DdsExtensible);
336        assert_eq!(Profile::default(), Profile::DdsExtensible);
337    }
338
339    #[test]
340    fn plain_dds_profile_constructor() {
341        let cfg = ParserConfig::plain_dds_4_2();
342        assert_eq!(cfg.profile, Profile::DdsBasic);
343        assert_eq!(cfg.compat, CompatMode::Strict);
344        assert_eq!(cfg.vendor, VendorExt::None);
345    }
346
347    #[test]
348    fn full_profile_constructor() {
349        let cfg = ParserConfig::full_4_2();
350        assert_eq!(cfg.profile, Profile::Full);
351    }
352
353    #[test]
354    fn strict_pragmatic_use_extensible_profile() {
355        // strict_4_2 + pragmatic_4_2 muessen identisch zu Default-Profil
356        // bleiben (kein implizites Upgrade auf Full).
357        assert_eq!(ParserConfig::strict_4_2().profile, Profile::DdsExtensible);
358        assert_eq!(
359            ParserConfig::pragmatic_4_2().profile,
360            Profile::DdsExtensible
361        );
362    }
363}