Skip to main content

zerodds_idl/features/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! IDL-Parser Feature-Flags (Spec-Completeness via Feature-Gating).
4//!
5//! Ziel: Spec-Completeness gegen OMG IDL 4.2. Alle Konstrukte (DDS-Profil,
6//! XTypes, CORBA, CCM, Template-Modules, Vendor-Erweiterungen) sind in der
7//! Default-Grammar als Productions vorhanden — sind aber per
8//! [`IdlFeatures`]-Bool-Flag aktivierbar. Bei Use eines Konstrukts dessen
9//! Feature OFF ist liefert der Validator einen
10//! `Error::FeatureNotEnabled`.
11//!
12//! # Profile-vs.-Features
13//!
14//! [`super::config::Profile`] ist ein hochlevel-Profil (`DdsBasic`,
15//! `DdsExtensible`, `Full`, `OpenSpliceLegacy`, ...). Jedes Profile
16//! mappt auf eine Default-`IdlFeatures`-Maske via
17//! [`IdlFeatures::for_profile`]. User kann zusaetzlich einzelne
18//! Feature-Bits togglen (z.B. `DdsExtensible` + `corba_template_modules`
19//! aktiv fuer DDS-RPC-Templates).
20
21pub mod gate;
22
23/// Feature-Flag-Set fuer den IDL-Parser.
24///
25/// Default ist DDS-Extensible (Plain DDS + XTypes + DDS-RPC), jeweils
26/// ohne CORBA/CCM-Konstrukte und ohne Vendor-Erweiterungen.
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub struct IdlFeatures {
29    // ========================================================================
30    // §7.4.5 — Value Types Full
31    // ========================================================================
32    /// `valuetype` mit `state_member`, `init_dcl`, `factory`-Body
33    /// (Spec §7.4.5 Rules 100-109). Box/Forward sind immer aktiv.
34    pub corba_value_types_full: bool,
35    /// `abstract valuetype`, `custom valuetype`, `truncatable`
36    /// (Spec §7.4.5 Rules 125-132).
37    pub corba_value_types_extras: bool,
38
39    // ========================================================================
40    // §7.4.6 — Repository-IDs
41    // ========================================================================
42    /// `typeid` und `typeprefix` Top-Level-Direktiven (Rules 111-117).
43    pub corba_repository_ids: bool,
44    /// `import` Top-Level-Direktive (Rules 115-117).
45    pub corba_import: bool,
46
47    // ========================================================================
48    // §7.4.7 — CORBA Interface-Erweiterungen
49    // ========================================================================
50    /// `local interface` (Rule 119).
51    pub corba_local_interface: bool,
52    /// `abstract interface`.
53    pub corba_abstract_interface: bool,
54    /// `: Object` als Base.
55    pub corba_object_base: bool,
56    /// `oneway` als Prefix vor Op (statt `@oneway`-Annotation).
57    pub corba_oneway_op: bool,
58    /// `context (...)` Clause (Rule 123/124).
59    pub corba_context: bool,
60
61    // ========================================================================
62    // §7.4.9 — Components/Homes/Ports
63    // ========================================================================
64    /// `component`/`provides`/`uses` (Rules 133+).
65    pub corba_components: bool,
66    /// `home`/`manages`/`primarykey`/`factory`/`finder` (Rules 145+).
67    pub corba_homes: bool,
68    /// `eventtype`/`emits`/`publishes`/`consumes`.
69    pub corba_eventtypes: bool,
70    /// `porttype`/`port`/`mirrorport`/`connector`.
71    pub corba_ports: bool,
72
73    // ========================================================================
74    // §7.4.12 — Template Modules
75    // ========================================================================
76    /// `module M<typename T> { ... };` und Template-Instantiation.
77    pub corba_template_modules: bool,
78
79    // ========================================================================
80    // §7.4.1.3 — Native-Type
81    // ========================================================================
82    /// `native <ident>;` (Rule 61). Implementiert; per Default an,
83    /// abschaltbar fuer Plain-DDS-Profil.
84    pub corba_native: bool,
85
86    // ========================================================================
87    // §7.3 — Preprocessor Stufe 2
88    // ========================================================================
89    /// Function-like Macros + Token-Pasting `##` + `#if`/`#elif` mit
90    /// Expression-Evaluation.
91    pub preprocessor_full: bool,
92    /// `#warning` und `#line`-Direktiven.
93    pub preprocessor_warning_line: bool,
94
95    // ========================================================================
96    // Vendor-Extensions
97    // ========================================================================
98    /// RTI Connext: `@RTI_*`-Annotations, `keylist`-Direktive,
99    /// `//@key`-Legacy-Comments.
100    pub vendor_rti: bool,
101    /// OpenSplice (modern, post-2010): `#pragma keylist` plus
102    /// modernere `@key`-Annotations.
103    pub vendor_opensplice: bool,
104    /// OpenSplice Legacy (pre-2010): `#pragma keylist`,
105    /// `#pragma DCPS_DATA_TYPE`, `#pragma DCPS_DATA_KEY`,
106    /// `#pragma cats`, `#pragma genequality`, CORBA-Style-IDLs.
107    /// Migration-Use-Case fuer Referenz-Kunden.
108    pub vendor_opensplice_legacy: bool,
109    /// Cyclone DDS: `#pragma keylist`, `idlc`-spezifische Annotations.
110    pub vendor_cyclonedds: bool,
111    /// FastDDS (eProsima): `@RTPS_*`-Annotations, `@key`-Variants.
112    pub vendor_fastdds: bool,
113}
114
115impl Default for IdlFeatures {
116    /// Default: DDS-Extensible-Profil (Plain DDS + XTypes + DDS-RPC,
117    /// Native-Type, keine CORBA/CCM, keine Vendor-Erweiterungen).
118    fn default() -> Self {
119        Self::dds_extensible()
120    }
121}
122
123impl IdlFeatures {
124    /// Alle Feature-Flags off — striktes Plain-DDS-Profil ohne
125    /// jegliche Erweiterung.
126    #[must_use]
127    pub const fn none() -> Self {
128        Self {
129            corba_value_types_full: false,
130            corba_value_types_extras: false,
131            corba_repository_ids: false,
132            corba_import: false,
133            corba_local_interface: false,
134            corba_abstract_interface: false,
135            corba_object_base: false,
136            corba_oneway_op: false,
137            corba_context: false,
138            corba_components: false,
139            corba_homes: false,
140            corba_eventtypes: false,
141            corba_ports: false,
142            corba_template_modules: false,
143            corba_native: false,
144            preprocessor_full: false,
145            preprocessor_warning_line: false,
146            vendor_rti: false,
147            vendor_opensplice: false,
148            vendor_opensplice_legacy: false,
149            vendor_cyclonedds: false,
150            vendor_fastdds: false,
151        }
152    }
153
154    /// Alle Feature-Flags on — Full-IDL-4.2 inkl. CORBA/CCM und
155    /// alle Vendor-Erweiterungen aktiv. Fuer Migration-Use-Cases
156    /// und Spec-Compliance-Audits.
157    #[must_use]
158    pub const fn all() -> Self {
159        Self {
160            corba_value_types_full: true,
161            corba_value_types_extras: true,
162            corba_repository_ids: true,
163            corba_import: true,
164            corba_local_interface: true,
165            corba_abstract_interface: true,
166            corba_object_base: true,
167            corba_oneway_op: true,
168            corba_context: true,
169            corba_components: true,
170            corba_homes: true,
171            corba_eventtypes: true,
172            corba_ports: true,
173            corba_template_modules: true,
174            corba_native: true,
175            preprocessor_full: true,
176            preprocessor_warning_line: true,
177            vendor_rti: true,
178            vendor_opensplice: true,
179            vendor_opensplice_legacy: true,
180            vendor_cyclonedds: true,
181            vendor_fastdds: true,
182        }
183    }
184
185    /// Plain DDS — minimaler DDS-Topic-Type-Umfang. Native-Type aus,
186    /// keine CORBA/CCM, keine Vendor-Erweiterungen.
187    #[must_use]
188    pub const fn dds_basic() -> Self {
189        Self::none()
190    }
191
192    /// DDS + XTypes + DDS-RPC: Native-Type aktiv, alles andere CORBA-
193    /// spezifische aus, keine Vendor-Erweiterungen. Default fuer den
194    /// ZeroDDS-Use-Case.
195    #[must_use]
196    pub const fn dds_extensible() -> Self {
197        let mut f = Self::none();
198        f.corba_native = true;
199        f
200    }
201
202    /// CORBA-Full: alle CORBA/CCM-Konstrukte aktiv (Value-Types Full,
203    /// Repository-IDs, Components, Homes, Ports, Template-Modules,
204    /// alle Interface-Erweiterungen). Keine Vendor-Erweiterungen.
205    #[must_use]
206    pub const fn corba_full() -> Self {
207        Self {
208            corba_value_types_full: true,
209            corba_value_types_extras: true,
210            corba_repository_ids: true,
211            corba_import: true,
212            corba_local_interface: true,
213            corba_abstract_interface: true,
214            corba_object_base: true,
215            corba_oneway_op: true,
216            corba_context: true,
217            corba_components: true,
218            corba_homes: true,
219            corba_eventtypes: true,
220            corba_ports: true,
221            corba_template_modules: true,
222            corba_native: true,
223            preprocessor_full: true,
224            preprocessor_warning_line: true,
225            vendor_rti: false,
226            vendor_opensplice: false,
227            vendor_opensplice_legacy: false,
228            vendor_cyclonedds: false,
229            vendor_fastdds: false,
230        }
231    }
232
233    /// §9.2.1 Plain-CORBA-Profile: Core, Any, Interfaces-Basic,
234    /// Interfaces-Full, Value-Types, CORBA-Specific-Interfaces,
235    /// CORBA-Specific-Value-Types — 7 Building-Blocks.
236    #[must_use]
237    pub const fn plain_corba() -> Self {
238        Self {
239            corba_value_types_full: true,
240            corba_value_types_extras: true,
241            corba_repository_ids: true,
242            corba_import: true,
243            corba_local_interface: true,
244            corba_abstract_interface: true,
245            corba_object_base: true,
246            corba_oneway_op: true,
247            corba_context: true,
248            corba_components: false,
249            corba_homes: false,
250            corba_eventtypes: false,
251            corba_ports: false,
252            corba_template_modules: false,
253            corba_native: true,
254            preprocessor_full: true,
255            preprocessor_warning_line: false,
256            vendor_rti: false,
257            vendor_opensplice: false,
258            vendor_opensplice_legacy: false,
259            vendor_cyclonedds: false,
260            vendor_fastdds: false,
261        }
262    }
263
264    /// §9.2.2 Minimum-CORBA-Profile: 4 Building-Blocks (Core,
265    /// Interfaces-Basic, Interfaces-Full, CORBA-Specific-Interfaces).
266    /// Kein Any, keine Value-Types.
267    #[must_use]
268    pub const fn minimum_corba() -> Self {
269        Self {
270            corba_value_types_full: false,
271            corba_value_types_extras: false,
272            corba_repository_ids: true,
273            corba_import: true,
274            corba_local_interface: true,
275            corba_abstract_interface: false,
276            corba_object_base: true,
277            corba_oneway_op: true,
278            corba_context: true,
279            corba_components: false,
280            corba_homes: false,
281            corba_eventtypes: false,
282            corba_ports: false,
283            corba_template_modules: false,
284            corba_native: true,
285            preprocessor_full: true,
286            preprocessor_warning_line: false,
287            vendor_rti: false,
288            vendor_opensplice: false,
289            vendor_opensplice_legacy: false,
290            vendor_cyclonedds: false,
291            vendor_fastdds: false,
292        }
293    }
294
295    /// §9.2.3 CCM-Profile: 9 Building-Blocks
296    /// (Plain-CORBA + Components-Basic + CCM-Specific).
297    #[must_use]
298    pub const fn ccm_profile() -> Self {
299        let mut f = Self::plain_corba();
300        f.corba_components = true;
301        f.corba_homes = true;
302        f.corba_eventtypes = true;
303        f
304    }
305
306    /// §9.2.4 IDL3+-Profile (CCM with Generic Interaction Support):
307    /// 11 Building-Blocks (CCM + Components-Ports/Connectors +
308    /// Template-Modules).
309    #[must_use]
310    pub const fn ccm_with_gis() -> Self {
311        let mut f = Self::ccm_profile();
312        f.corba_ports = true;
313        f.corba_template_modules = true;
314        f
315    }
316
317    /// §9.3.3 RPC-over-DDS-Profile: 7 BBs/Groups (Core, Extended-Data-
318    /// Types, Anonymous, Interfaces-Basic, Annotations, General-Purpose,
319    /// Interfaces-Group). Komplexer als pure DDS, weil Interface-BB
320    /// aktiv.
321    #[must_use]
322    pub const fn rpc_over_dds() -> Self {
323        let mut f = Self::dds_extensible();
324        f.corba_oneway_op = true;
325        f.preprocessor_full = true;
326        f
327    }
328
329    /// OpenSplice-Legacy-Profil: CORBA-Full + OpenSplice-Legacy-Pragmas +
330    /// Preprocessor-Stufe-2. Migration-Use-Case fuer Referenz-Kunde,
331    /// der eine alte OpenSplice-Variante upgradet.
332    #[must_use]
333    pub const fn opensplice_legacy() -> Self {
334        let mut f = Self::corba_full();
335        f.vendor_opensplice_legacy = true;
336        f.vendor_opensplice = true;
337        f
338    }
339
340    /// OpenSplice-Modern-Profil: DDS-Extensible + OpenSplice-Pragmas.
341    #[must_use]
342    pub const fn opensplice_modern() -> Self {
343        let mut f = Self::dds_extensible();
344        f.vendor_opensplice = true;
345        f.preprocessor_full = true;
346        f
347    }
348
349    /// RTI-Connext-Profil: DDS-Extensible + RTI-Annotations und
350    /// `keylist`-Pragma.
351    #[must_use]
352    pub const fn rti_connext() -> Self {
353        let mut f = Self::dds_extensible();
354        f.vendor_rti = true;
355        f.preprocessor_full = true;
356        f
357    }
358
359    /// Cyclone-DDS-Profil: DDS-Extensible + Cyclone-Pragmas.
360    #[must_use]
361    pub const fn cyclonedds() -> Self {
362        let mut f = Self::dds_extensible();
363        f.vendor_cyclonedds = true;
364        f
365    }
366
367    /// FastDDS-Profil: DDS-Extensible + FastDDS-Annotations.
368    #[must_use]
369    pub const fn fastdds() -> Self {
370        let mut f = Self::dds_extensible();
371        f.vendor_fastdds = true;
372        f
373    }
374
375    /// Aktivieren eines Vendor-Profils zusaetzlich zur aktuellen Maske
376    /// (additiv).
377    #[must_use]
378    pub const fn with_vendor_rti(mut self) -> Self {
379        self.vendor_rti = true;
380        self
381    }
382
383    /// Additiv `vendor_opensplice_legacy` aktivieren.
384    #[must_use]
385    pub const fn with_opensplice_legacy(mut self) -> Self {
386        self.vendor_opensplice_legacy = true;
387        self.vendor_opensplice = true;
388        self
389    }
390
391    /// Additiv `vendor_cyclonedds` aktivieren.
392    #[must_use]
393    pub const fn with_cyclonedds(mut self) -> Self {
394        self.vendor_cyclonedds = true;
395        self
396    }
397
398    /// Additiv `vendor_fastdds` aktivieren.
399    #[must_use]
400    pub const fn with_fastdds(mut self) -> Self {
401        self.vendor_fastdds = true;
402        self
403    }
404}
405
406#[cfg(test)]
407mod tests {
408    use super::*;
409
410    #[test]
411    fn default_is_dds_extensible() {
412        assert_eq!(IdlFeatures::default(), IdlFeatures::dds_extensible());
413    }
414
415    #[test]
416    fn none_disables_everything() {
417        let f = IdlFeatures::none();
418        assert!(!f.corba_value_types_full);
419        assert!(!f.corba_components);
420        assert!(!f.corba_native);
421        assert!(!f.vendor_rti);
422        assert!(!f.preprocessor_full);
423    }
424
425    #[test]
426    fn all_enables_everything() {
427        let f = IdlFeatures::all();
428        assert!(f.corba_value_types_full);
429        assert!(f.corba_components);
430        assert!(f.corba_template_modules);
431        assert!(f.vendor_rti);
432        assert!(f.vendor_opensplice_legacy);
433        assert!(f.preprocessor_full);
434    }
435
436    #[test]
437    fn dds_basic_has_no_corba() {
438        let f = IdlFeatures::dds_basic();
439        assert!(!f.corba_value_types_full);
440        assert!(!f.corba_native);
441        assert!(!f.corba_components);
442    }
443
444    #[test]
445    fn dds_extensible_has_native_only() {
446        let f = IdlFeatures::dds_extensible();
447        assert!(f.corba_native);
448        assert!(!f.corba_value_types_full);
449        assert!(!f.corba_components);
450        assert!(!f.vendor_rti);
451    }
452
453    #[test]
454    fn corba_full_has_all_corba_no_vendor() {
455        let f = IdlFeatures::corba_full();
456        assert!(f.corba_value_types_full);
457        assert!(f.corba_components);
458        assert!(f.corba_template_modules);
459        assert!(f.preprocessor_full);
460        assert!(!f.vendor_rti);
461        assert!(!f.vendor_opensplice_legacy);
462    }
463
464    #[test]
465    fn opensplice_legacy_includes_corba_and_legacy_pragmas() {
466        let f = IdlFeatures::opensplice_legacy();
467        assert!(f.corba_value_types_full);
468        assert!(f.corba_components);
469        assert!(f.vendor_opensplice_legacy);
470        assert!(f.vendor_opensplice);
471    }
472
473    #[test]
474    fn opensplice_modern_is_extensible_plus_pragmas() {
475        let f = IdlFeatures::opensplice_modern();
476        assert!(f.corba_native);
477        assert!(f.vendor_opensplice);
478        assert!(!f.vendor_opensplice_legacy);
479        assert!(!f.corba_components);
480    }
481
482    #[test]
483    fn rti_connext_profile() {
484        let f = IdlFeatures::rti_connext();
485        assert!(f.corba_native);
486        assert!(f.vendor_rti);
487        assert!(!f.vendor_opensplice);
488    }
489
490    #[test]
491    fn cyclonedds_profile() {
492        let f = IdlFeatures::cyclonedds();
493        assert!(f.corba_native);
494        assert!(f.vendor_cyclonedds);
495        assert!(!f.vendor_rti);
496    }
497
498    #[test]
499    fn fastdds_profile() {
500        let f = IdlFeatures::fastdds();
501        assert!(f.corba_native);
502        assert!(f.vendor_fastdds);
503        assert!(!f.vendor_opensplice);
504    }
505
506    #[test]
507    fn additive_with_vendor_rti() {
508        let f = IdlFeatures::dds_extensible().with_vendor_rti();
509        assert!(f.vendor_rti);
510        assert!(f.corba_native);
511    }
512
513    #[test]
514    fn additive_with_opensplice_legacy_sets_both() {
515        let f = IdlFeatures::dds_extensible().with_opensplice_legacy();
516        assert!(f.vendor_opensplice);
517        assert!(f.vendor_opensplice_legacy);
518    }
519
520    // §9 Profile-Constructors
521
522    #[test]
523    fn plain_corba_profile_matches_spec() {
524        // §9.2.1: 7 BBs (Core, Any, Interfaces-Basic+Full,
525        // Value-Types, CORBA-Specific-Interfaces+Value-Types).
526        let f = IdlFeatures::plain_corba();
527        assert!(f.corba_value_types_full);
528        assert!(f.corba_repository_ids);
529        assert!(f.corba_native);
530        assert!(!f.corba_components);
531        assert!(!f.corba_homes);
532        assert!(!f.corba_template_modules);
533    }
534
535    #[test]
536    fn minimum_corba_profile_matches_spec() {
537        // §9.2.2: 4 BBs (Core, Interfaces-Basic+Full,
538        // CORBA-Specific-Interfaces). Kein Any, keine Value-Types.
539        let f = IdlFeatures::minimum_corba();
540        assert!(!f.corba_value_types_full);
541        assert!(!f.corba_value_types_extras);
542        assert!(f.corba_repository_ids);
543        assert!(f.corba_oneway_op);
544        assert!(!f.corba_components);
545    }
546
547    #[test]
548    fn ccm_profile_matches_spec() {
549        // §9.2.3: Plain-CORBA + Components-Basic + CCM-Specific.
550        let f = IdlFeatures::ccm_profile();
551        assert!(f.corba_value_types_full);
552        assert!(f.corba_components);
553        assert!(f.corba_homes);
554        assert!(f.corba_eventtypes);
555        assert!(!f.corba_ports);
556        assert!(!f.corba_template_modules);
557    }
558
559    #[test]
560    fn ccm_with_gis_profile_matches_spec() {
561        // §9.2.4 (IDL3+): CCM + Ports/Connectors + Template-Modules.
562        let f = IdlFeatures::ccm_with_gis();
563        assert!(f.corba_components);
564        assert!(f.corba_ports);
565        assert!(f.corba_template_modules);
566    }
567
568    #[test]
569    fn rpc_over_dds_profile_matches_spec() {
570        // §9.3.3: DDS-Extensible + Interfaces-Basic-Annotations.
571        let f = IdlFeatures::rpc_over_dds();
572        assert!(f.corba_native);
573        assert!(f.corba_oneway_op);
574        assert!(!f.corba_components);
575    }
576
577    #[test]
578    fn plain_dds_profile_matches_spec_exactly() {
579        // §9.3.1: Plain-DDS = Core + Anonymous Types only.
580        let f = IdlFeatures::dds_basic();
581        assert!(!f.corba_native);
582        assert!(!f.corba_components);
583        assert!(!f.corba_value_types_full);
584        assert!(!f.vendor_rti);
585        assert!(!f.vendor_cyclonedds);
586    }
587
588    #[test]
589    fn dds_extensible_excludes_corba_components() {
590        // §9.3.2: DDS-Extensible aktiviert Annotations-Groups, aber
591        // keine CORBA-Components.
592        let f = IdlFeatures::dds_extensible();
593        assert!(f.corba_native);
594        assert!(!f.corba_components);
595        assert!(!f.corba_homes);
596        assert!(!f.corba_template_modules);
597    }
598}