Skip to main content

zerodds_dlrl_codegen/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3
4//! DLRL Code-Generation-Helpers — DDS 1.4 §B.4.
5//!
6//! Crate `zerodds-dlrl-codegen`. Safety classification: **STANDARD**.
7//!
8//! Erzeugt sprachspezifische Boilerplate fuer die DLRL-Pragmas
9//! (`DCPS_DATA_TYPE`, `DCPS_DATA_KEY`, `DCPS_DLRL_RELATION`).
10//!
11//! # Konsumiert
12//!
13//! Eine Liste von [`zerodds_dlrl::pragma::DlrlPragma`]-Werten, die der
14//! Frontend-Parser bereits validiert hat. Aus diesen Pragmas wird
15//! pro `DCPS_DATA_TYPE` ein Home-Class + Object-Class generiert,
16//! pro `DCPS_DATA_KEY` ein Key-Field-Hint, pro `DCPS_DLRL_RELATION`
17//! eine Relationship-Accessor-Methode.
18//!
19//! # Backends
20//!
21//! * `cpp`   — C++ Headers + Inline-Implementations.
22//! * `csharp`— C# Partial-Classes mit `[DlrlObject]`-Attributes.
23//! * `java`  — Java-Interfaces + Skeleton-Implementations.
24//! * `ts`    — TypeScript-Interfaces + Class-Skeletons.
25
26#![cfg_attr(not(feature = "std"), no_std)]
27#![forbid(unsafe_code)]
28#![warn(missing_docs)]
29
30extern crate alloc;
31
32pub mod cpp;
33pub mod csharp;
34pub mod java;
35pub mod ts;
36
37pub use cpp::{generate_cpp_home, generate_cpp_object};
38pub use csharp::{generate_csharp_object, generate_csharp_partial};
39pub use java::{generate_java_object, generate_java_object_listener};
40pub use ts::{generate_ts_class, generate_ts_interface};
41
42use alloc::string::String;
43use alloc::vec::Vec;
44use zerodds_dlrl::pragma::DlrlPragma;
45
46/// Aggregierte Type-Info — alle Pragmas, die einen einzelnen Type
47/// betreffen.
48#[derive(Debug, Clone, PartialEq, Eq, Default)]
49pub struct DlrlTypeInfo {
50    /// Vollqualifizierter Type-Name (`demo::Trade`).
51    pub name: String,
52    /// Liste der Key-Felder (Reihenfolge wie im IDL).
53    pub keys: Vec<String>,
54    /// Liste der Relationships (relation_name, target_type).
55    pub relations: Vec<(String, String)>,
56}
57
58/// Sammelt aus einer flachen Pragma-Liste eine `DlrlTypeInfo`-Map
59/// (key = Type-Name).
60#[must_use]
61pub fn collect_type_infos(
62    pragmas: &[DlrlPragma],
63) -> alloc::collections::BTreeMap<String, DlrlTypeInfo> {
64    use alloc::collections::BTreeMap;
65    let mut out: BTreeMap<String, DlrlTypeInfo> = BTreeMap::new();
66    for p in pragmas {
67        match p {
68            DlrlPragma::DataType { name } => {
69                out.entry(name.clone()).or_insert_with(|| DlrlTypeInfo {
70                    name: name.clone(),
71                    ..DlrlTypeInfo::default()
72                });
73            }
74            DlrlPragma::DataKey { type_name, field } => {
75                out.entry(type_name.clone())
76                    .or_insert_with(|| DlrlTypeInfo {
77                        name: type_name.clone(),
78                        ..DlrlTypeInfo::default()
79                    })
80                    .keys
81                    .push(field.clone());
82            }
83            DlrlPragma::DlrlRelation {
84                type_name,
85                relation,
86                target,
87            } => {
88                out.entry(type_name.clone())
89                    .or_insert_with(|| DlrlTypeInfo {
90                        name: type_name.clone(),
91                        ..DlrlTypeInfo::default()
92                    })
93                    .relations
94                    .push((relation.clone(), target.clone()));
95            }
96        }
97    }
98    out
99}
100
101#[cfg(test)]
102#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn collect_groups_by_type_name() {
108        let pragmas = alloc::vec![
109            DlrlPragma::DataType {
110                name: "demo::Trade".into()
111            },
112            DlrlPragma::DataKey {
113                type_name: "demo::Trade".into(),
114                field: "symbol".into(),
115            },
116            DlrlPragma::DataKey {
117                type_name: "demo::Trade".into(),
118                field: "venue".into(),
119            },
120            DlrlPragma::DlrlRelation {
121                type_name: "demo::Trade".into(),
122                relation: "quotes".into(),
123                target: "demo::Quote".into(),
124            },
125        ];
126        let infos = collect_type_infos(&pragmas);
127        assert_eq!(infos.len(), 1);
128        let trade = infos.get("demo::Trade").unwrap();
129        assert_eq!(
130            trade.keys,
131            alloc::vec!["symbol".to_string(), "venue".into()]
132        );
133        assert_eq!(trade.relations.len(), 1);
134        assert_eq!(trade.relations[0].0, "quotes");
135    }
136
137    #[test]
138    fn data_type_without_keys_yields_empty_lists() {
139        let pragmas = alloc::vec![DlrlPragma::DataType { name: "Foo".into() }];
140        let infos = collect_type_infos(&pragmas);
141        assert!(infos.get("Foo").unwrap().keys.is_empty());
142        assert!(infos.get("Foo").unwrap().relations.is_empty());
143    }
144
145    #[test]
146    fn keys_without_data_type_still_create_info() {
147        let pragmas = alloc::vec![DlrlPragma::DataKey {
148            type_name: "Bar".into(),
149            field: "k".into(),
150        }];
151        let infos = collect_type_infos(&pragmas);
152        assert!(infos.contains_key("Bar"));
153        assert_eq!(infos["Bar"].keys, alloc::vec!["k".to_string()]);
154    }
155}