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//! Generates language-specific boilerplate for the DLRL pragmas
9//! (`DCPS_DATA_TYPE`, `DCPS_DATA_KEY`, `DCPS_DLRL_RELATION`).
10//!
11//! # Consumes
12//!
13//! A list of [`zerodds_dlrl::pragma::DlrlPragma`] values that the
14//! frontend parser has already validated. From these pragmas, a home
15//! class plus object class is generated per `DCPS_DATA_TYPE`, a key
16//! field hint per `DCPS_DATA_KEY`, and a relationship accessor method
17//! per `DCPS_DLRL_RELATION`.
18//!
19//! # Backends
20//!
21//! * `cpp`   — C++ headers + inline implementations.
22//! * `csharp`— C# partial classes with `[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/// Aggregated type info — all pragmas that pertain to a single type.
47#[derive(Debug, Clone, PartialEq, Eq, Default)]
48pub struct DlrlTypeInfo {
49    /// Fully qualified type name (`demo::Trade`).
50    pub name: String,
51    /// List of key fields (order as in the IDL).
52    pub keys: Vec<String>,
53    /// List of relationships (relation_name, target_type).
54    pub relations: Vec<(String, String)>,
55}
56
57/// Collects a `DlrlTypeInfo` map from a flat pragma list
58/// (key = type name).
59#[must_use]
60pub fn collect_type_infos(
61    pragmas: &[DlrlPragma],
62) -> alloc::collections::BTreeMap<String, DlrlTypeInfo> {
63    use alloc::collections::BTreeMap;
64    let mut out: BTreeMap<String, DlrlTypeInfo> = BTreeMap::new();
65    for p in pragmas {
66        match p {
67            DlrlPragma::DataType { name } => {
68                out.entry(name.clone()).or_insert_with(|| DlrlTypeInfo {
69                    name: name.clone(),
70                    ..DlrlTypeInfo::default()
71                });
72            }
73            DlrlPragma::DataKey { type_name, field } => {
74                out.entry(type_name.clone())
75                    .or_insert_with(|| DlrlTypeInfo {
76                        name: type_name.clone(),
77                        ..DlrlTypeInfo::default()
78                    })
79                    .keys
80                    .push(field.clone());
81            }
82            DlrlPragma::DlrlRelation {
83                type_name,
84                relation,
85                target,
86            } => {
87                out.entry(type_name.clone())
88                    .or_insert_with(|| DlrlTypeInfo {
89                        name: type_name.clone(),
90                        ..DlrlTypeInfo::default()
91                    })
92                    .relations
93                    .push((relation.clone(), target.clone()));
94            }
95        }
96    }
97    out
98}
99
100#[cfg(test)]
101#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn collect_groups_by_type_name() {
107        let pragmas = alloc::vec![
108            DlrlPragma::DataType {
109                name: "demo::Trade".into()
110            },
111            DlrlPragma::DataKey {
112                type_name: "demo::Trade".into(),
113                field: "symbol".into(),
114            },
115            DlrlPragma::DataKey {
116                type_name: "demo::Trade".into(),
117                field: "venue".into(),
118            },
119            DlrlPragma::DlrlRelation {
120                type_name: "demo::Trade".into(),
121                relation: "quotes".into(),
122                target: "demo::Quote".into(),
123            },
124        ];
125        let infos = collect_type_infos(&pragmas);
126        assert_eq!(infos.len(), 1);
127        let trade = infos.get("demo::Trade").unwrap();
128        assert_eq!(
129            trade.keys,
130            alloc::vec!["symbol".to_string(), "venue".into()]
131        );
132        assert_eq!(trade.relations.len(), 1);
133        assert_eq!(trade.relations[0].0, "quotes");
134    }
135
136    #[test]
137    fn data_type_without_keys_yields_empty_lists() {
138        let pragmas = alloc::vec![DlrlPragma::DataType { name: "Foo".into() }];
139        let infos = collect_type_infos(&pragmas);
140        assert!(infos.get("Foo").unwrap().keys.is_empty());
141        assert!(infos.get("Foo").unwrap().relations.is_empty());
142    }
143
144    #[test]
145    fn keys_without_data_type_still_create_info() {
146        let pragmas = alloc::vec![DlrlPragma::DataKey {
147            type_name: "Bar".into(),
148            field: "k".into(),
149        }];
150        let infos = collect_type_infos(&pragmas);
151        assert!(infos.contains_key("Bar"));
152        assert_eq!(infos["Bar"].keys, alloc::vec!["k".to_string()]);
153    }
154}