1use super::cpp::{NATIVE_NAMESPACE, cpp_type_path, cxx_name, module_names, without_native_prefix};
33use crate::adapter::c::ordered_types;
34use crate::adapter::cst::MaterialiseError;
35use zerodds_ast::{Ast, CaseLabel, CharWidth, MemberNode, PrimitiveKind, TypeKind, TypeNode};
36use zerodds_ast_core::{Id, Ref};
37
38pub fn psm_path(ast: &Ast, id: Id<TypeNode>) -> String {
40 render_path(&without_native_prefix(|| cpp_type_path(ast, id)))
41}
42
43pub fn native_path(ast: &Ast, id: Id<TypeNode>) -> String {
45 let path = without_native_prefix(|| cpp_type_path(ast, id));
46 let rendered = render_path(&path);
47 format!("::{NATIVE_NAMESPACE}{rendered}")
48}
49
50fn render_path(path: &zerodds_lang_cst::common::Path) -> String {
51 let segments: Vec<&str> = path
52 .segments()
53 .iter()
54 .map(zerodds_lang_cst::Ident::as_str)
55 .collect();
56 format!("::{}", segments.join("::"))
57}
58
59fn no_form(construct: &str, at: &str) -> MaterialiseError {
60 MaterialiseError::NoLanguageForm {
61 construct: construct.to_string(),
62 language: "C++ (PSM mapping)",
63 at: at.to_string(),
64 }
65}
66
67pub fn psm_type(ast: &Ast, id: Id<TypeNode>, at: &str) -> Result<String, MaterialiseError> {
73 psm_type_at(ast, id, at, 0)
74}
75
76const MAX_DEPTH: usize = 64;
77
78fn psm_type_at(
80 ast: &Ast,
81 id: Id<TypeNode>,
82 at: &str,
83 depth: usize,
84) -> Result<String, MaterialiseError> {
85 if depth > MAX_DEPTH {
86 return Err(MaterialiseError::Unevaluated {
87 construct: format!("type spelling — it nests past {MAX_DEPTH}"),
88 at: at.to_string(),
89 });
90 }
91 let Ok(node) = ast.type_(id) else {
92 return Err(no_form("a type the model would not hand over", at));
93 };
94 let element = |r: &Ref<TypeNode>| -> Result<String, MaterialiseError> {
95 let Some(target) = r.resolved() else {
96 return Err(no_form("an unresolved element type", at));
97 };
98 psm_type_at(ast, target, at, depth + 1)
99 };
100 Ok(match &node.kind {
101 TypeKind::Primitive(kind) => primitive(*kind).to_string(),
102 TypeKind::String {
103 width: CharWidth::WChar,
104 ..
105 } => "std::wstring".to_string(),
106 TypeKind::String { .. } | TypeKind::Fixed { .. } => "std::string".to_string(),
107 TypeKind::Sequence { element: e, .. } => format!("std::vector<{}>", element(e)?),
108 TypeKind::Array { element: e, dims } => {
109 let mut out = element(e)?;
110 for dim in dims.iter().rev() {
111 let length =
112 ast.eval_integral(*dim)
113 .map_err(|_| MaterialiseError::Unevaluated {
114 construct: "array length".to_string(),
115 at: at.to_string(),
116 })?;
117 out = format!("::dds::core::array<{out}, {length}>");
118 }
119 out
120 }
121 TypeKind::Map { key, value, .. } => {
122 format!("std::map<{}, {}>", element(key)?, element(value)?)
123 }
124 TypeKind::Alias { aliased } if node.name.is_empty() => element(aliased)?,
125 TypeKind::Structure { .. }
126 | TypeKind::Union { .. }
127 | TypeKind::Enumeration { .. }
128 | TypeKind::Bitmask { .. }
129 | TypeKind::Bitset { .. }
130 | TypeKind::Alias { .. }
131 | TypeKind::Native
132 | TypeKind::ValueBox { .. } => psm_path(ast, id),
133 _ => return Err(no_form("this construct", at)),
134 })
135}
136
137const fn primitive(kind: PrimitiveKind) -> &'static str {
139 match kind {
140 PrimitiveKind::Boolean => "bool",
141 PrimitiveKind::Char => "char",
142 PrimitiveKind::WChar => "wchar_t",
143 PrimitiveKind::Octet | PrimitiveKind::UInt8 => "uint8_t",
144 PrimitiveKind::Int8 => "int8_t",
145 PrimitiveKind::Int16 => "int16_t",
146 PrimitiveKind::UInt16 => "uint16_t",
147 PrimitiveKind::Int32 => "int32_t",
148 PrimitiveKind::UInt32 => "uint32_t",
149 PrimitiveKind::Int64 => "int64_t",
150 PrimitiveKind::UInt64 => "uint64_t",
151 PrimitiveKind::Float32 => "float",
152 PrimitiveKind::Float64 => "double",
153 PrimitiveKind::Float128 => "long double",
154 }
155}
156
157fn is_native_attribute(ast: &Ast, id: Id<TypeNode>) -> bool {
160 let mut current = id;
161 for _ in 0..MAX_DEPTH {
162 let Ok(node) = ast.type_(current) else {
163 return false;
164 };
165 match &node.kind {
166 TypeKind::Primitive(_) | TypeKind::Enumeration { .. } | TypeKind::Bitmask { .. } => {
167 return true;
168 }
169 TypeKind::Alias { aliased } => match aliased.resolved() {
170 Some(next) => current = next,
171 None => return false,
172 },
173 _ => return false,
174 }
175 }
176 false
177}
178
179struct Attribute {
181 name: String,
183 state: String,
185 type_: String,
187 native: bool,
189}
190
191fn attribute(ast: &Ast, member: &MemberNode, owner: &str) -> Result<Attribute, MaterialiseError> {
192 let Some(target) = member.type_.resolved() else {
193 return Err(MaterialiseError::Unevaluated {
194 construct: "member type".to_string(),
195 at: format!("{owner}::{}", member.name),
196 });
197 };
198 let inner = psm_type(ast, target, &member.name)?;
199 let name = cxx_name(&member.name);
200 let (type_, native) = match super::cpp::held(ast, member) {
202 super::cpp::Held::Shared => (format!("::dds::core::external<{inner}>"), false),
203 super::cpp::Held::Plain => (format!("::dds::core::optional<{inner}>"), false),
204 super::cpp::Held::Value => (inner, is_native_attribute(ast, target)),
205 };
206 Ok(Attribute {
207 name: name.clone(),
208 state: name,
209 type_,
210 native,
211 })
212}
213
214fn name_state(attributes: &mut [Attribute]) {
216 let mut taken: Vec<String> = attributes.iter().map(|a| a.name.clone()).collect();
218 for attribute in attributes {
219 let mut state = format!("{}_", attribute.name);
220 while taken.contains(&state) {
221 state.push('_');
222 }
223 taken.push(state.clone());
224 attribute.state = state;
225 }
226}
227
228fn accessors(attribute: &Attribute) -> String {
229 let Attribute {
230 name, state, type_, ..
231 } = attribute;
232 if attribute.native {
233 format!(
234 " {type_} {name}() const {{ return {state}; }}\n {type_}& {name}() {{ return {state}; }}\n void {name}({type_} value) {{ {state} = value; }}\n"
235 )
236 } else {
237 format!(
238 " const {type_}& {name}() const {{ return {state}; }}\n {type_}& {name}() {{ return {state}; }}\n void {name}(const {type_}& value) {{ {state} = value; }}\n"
239 )
240 }
241}
242
243fn parameter(attribute: &Attribute) -> String {
244 if attribute.native {
245 format!("{} {}", attribute.type_, attribute.name)
246 } else {
247 format!("const {}& {}", attribute.type_, attribute.name)
248 }
249}
250
251fn structure(
253 ast: &Ast,
254 id: Id<TypeNode>,
255 node: &TypeNode,
256) -> Result<(String, String), MaterialiseError> {
257 let name = cxx_name(&node.name);
258 let base = ast
259 .base_of(id)
260 .filter(|_| matches!(node.kind, TypeKind::Structure { .. }));
261 let mut own = Vec::new();
262 for member in ast.members_of(id) {
263 let Ok(m) = ast.member(*member) else {
264 return Err(MaterialiseError::NoNode {
265 kind: "member",
266 at: node.name.clone(),
267 });
268 };
269 own.push(attribute(ast, m, &node.name)?);
270 }
271 name_state(&mut own);
272 let mut base_attributes = Vec::new();
274 if let Some(b) = base {
275 let base_node = ast.type_(b).map_err(|_| MaterialiseError::NoNode {
276 kind: "member",
277 at: node.name.clone(),
278 })?;
279 for member in ast.all_members_of(b) {
280 let Ok(m) = ast.member(member) else {
281 return Err(MaterialiseError::NoNode {
282 kind: "member",
283 at: base_node.name.clone(),
284 });
285 };
286 base_attributes.push(attribute(ast, m, &base_node.name)?);
287 }
288 }
289
290 let mut text = format!(
291 "/// `{}` as DDS-PSM-Cxx 1.0 \u{a7}8.1.1 maps it: a class with accessors (\u{a7}7.4.6).\nclass {name}{} {{\npublic:\n {name}() = default;\n",
292 node.name,
293 base.map_or(String::new(), |b| format!(" : public {}", psm_path(ast, b))),
294 );
295 let parameters: Vec<String> = base_attributes
296 .iter()
297 .chain(own.iter())
298 .map(parameter)
299 .collect();
300 if !parameters.is_empty() {
301 let explicit = if parameters.len() == 1 {
302 "explicit "
303 } else {
304 ""
305 };
306 let mut initialisers = Vec::new();
307 if let Some(b) = base {
308 let args: Vec<&str> = base_attributes.iter().map(|a| a.name.as_str()).collect();
309 initialisers.push(format!("{}({})", psm_path(ast, b), args.join(", ")));
310 }
311 for a in &own {
312 initialisers.push(format!("{}({})", a.state, a.name));
313 }
314 let init = if initialisers.is_empty() {
315 String::new()
316 } else {
317 format!("\n : {}", initialisers.join(", "))
318 };
319 text.push_str(&format!(
320 " {explicit}{name}({}){init} {{}}\n",
321 parameters.join(", ")
322 ));
323 }
324 text.push('\n');
325 for a in &own {
326 text.push_str(&accessors(a));
327 }
328 text.push_str(&format!("\n void swap({name}& other) {{\n"));
330 if let Some(b) = base {
331 text.push_str(&format!(" {}::swap(other);\n", psm_path(ast, b)));
332 }
333 if !own.is_empty() {
334 text.push_str(" using std::swap;\n");
335 }
336 for a in &own {
337 text.push_str(&format!(" swap({0}, other.{0});\n", a.state));
338 }
339 text.push_str(" }\n\n");
340 text.push_str(&format!(
342 " bool operator==(const {name}& other) const {{\n return "
343 ));
344 let mut terms: Vec<String> = Vec::new();
345 if let Some(b) = base {
346 terms.push(format!(
347 "static_cast<const {0}&>(*this) == static_cast<const {0}&>(other)",
348 psm_path(ast, b)
349 ));
350 }
351 for a in &own {
352 terms.push(format!("{0} == other.{0}", a.state));
353 }
354 if terms.is_empty() {
355 text.push_str("true");
356 } else {
357 text.push_str(&terms.join("\n && "));
358 }
359 text.push_str(&format!(
360 ";\n }}\n bool operator!=(const {name}& other) const {{ return !(*this == other); }}\n\nprivate:\n"
361 ));
362 for a in &own {
363 text.push_str(&format!(" {} {}{{}};\n", a.type_, a.state));
364 }
365 text.push_str("};\n");
366 text.push_str(&format!(
367 "\ninline void swap({name}& a, {name}& b) {{ a.swap(b); }}\n"
368 ));
369
370 let path = psm_path(ast, id);
372 let native = native_path(ast, id);
373 let mut all = Vec::new();
374 for member in ast.all_members_of(id) {
375 let Ok(m) = ast.member(member) else {
376 return Err(MaterialiseError::NoNode {
377 kind: "member",
378 at: node.name.clone(),
379 });
380 };
381 all.push(attribute(ast, m, &node.name)?);
382 }
383 let mut conv = format!(
384 "template <>\nstruct Conv<{path}> {{\n using Native = {native};\n static Native to_native(const {path}& v) {{\n Native n;\n"
385 );
386 for a in &all {
387 conv.push_str(&format!(
388 " n.{0} = Shape<decltype(n.{0}), typename Conv<{1}>::Native>::apply(Conv<{1}>::to_native(v.{0}()));\n",
389 a.name, a.type_
390 ));
391 }
392 conv.push_str(&format!(
393 " return n;\n }}\n static {path} from_native(const Native& n) {{\n {path} v;\n"
394 ));
395 for a in &all {
396 conv.push_str(&format!(
397 " v.{0}(Conv<{1}>::from_native(Shape<typename Conv<{1}>::Native, decltype(n.{0})>::apply(n.{0})));\n",
398 a.name, a.type_
399 ));
400 }
401 conv.push_str(" return v;\n }\n};\n");
402 Ok((text, conv))
403}
404
405struct UnionCase {
408 attribute: Attribute,
409 labels: Vec<i128>,
410 is_default: bool,
411}
412
413fn listed_values(ast: &Ast, disc: Id<TypeNode>) -> Result<Option<Vec<i128>>, MaterialiseError> {
417 let mut current = disc;
418 for _ in 0..MAX_DEPTH {
419 let Ok(node) = ast.type_(current) else {
420 return Err(MaterialiseError::NoNode {
421 kind: "discriminator type",
422 at: format!("{disc:?}"),
423 });
424 };
425 match &node.kind {
426 TypeKind::Alias { aliased } => match aliased.resolved() {
427 Some(next) => current = next,
428 None => {
429 return Err(MaterialiseError::Unevaluated {
430 construct: "typedef of the discriminator".to_string(),
431 at: node.name.clone(),
432 });
433 }
434 },
435 TypeKind::Primitive(PrimitiveKind::Boolean) => return Ok(Some(vec![0, 1])),
436 TypeKind::Enumeration { enumerators } => {
437 let mut values = Vec::new();
438 let mut next: i128 = 0;
439 for e in enumerators {
440 let Ok(e) = ast.enumerator(*e) else {
441 return Err(MaterialiseError::NoNode {
442 kind: "enumerator",
443 at: node.name.clone(),
444 });
445 };
446 let value = match e.value {
447 Some(expr) => {
448 ast.eval_integral(expr)
449 .map_err(|_| MaterialiseError::Unevaluated {
450 construct: "enumerator value".to_string(),
451 at: node.name.clone(),
452 })?
453 }
454 None => next,
455 };
456 values.push(value);
457 next = value + 1;
458 }
459 return Ok(Some(values));
460 }
461 _ => return Ok(None),
462 }
463 }
464 Ok(None)
465}
466
467fn unused_discriminator(
470 ast: &Ast,
471 disc: Id<TypeNode>,
472 used: &[i128],
473) -> Result<Option<i128>, MaterialiseError> {
474 if let Some(values) = listed_values(ast, disc)? {
475 return Ok(values.into_iter().find(|v| !used.contains(v)));
476 }
477 let mut candidate: i128 = 0;
479 while used.contains(&candidate) {
480 candidate += 1;
481 }
482 Ok(Some(candidate))
483}
484
485fn union_class(
490 ast: &Ast,
491 id: Id<TypeNode>,
492 node: &TypeNode,
493 discriminator: &Ref<TypeNode>,
494) -> Result<(String, String), MaterialiseError> {
495 let name = cxx_name(&node.name);
496 let Some(disc) = discriminator.resolved() else {
497 return Err(MaterialiseError::Unevaluated {
498 construct: "union discriminator".to_string(),
499 at: node.name.clone(),
500 });
501 };
502 let disc_type = psm_type(ast, disc, &node.name)?;
503 let mut cases: Vec<UnionCase> = Vec::new();
504 for case in ast.cases_of(id) {
505 let Ok(case_node) = ast.case(*case) else {
506 return Err(MaterialiseError::NoNode {
507 kind: "case",
508 at: node.name.clone(),
509 });
510 };
511 let Ok(member) = ast.member(case_node.member) else {
512 return Err(MaterialiseError::NoNode {
513 kind: "member",
514 at: node.name.clone(),
515 });
516 };
517 let mut labels = Vec::new();
518 let mut is_default = false;
519 for label in &case_node.labels {
520 match label {
521 CaseLabel::Value(expr) => labels.push(ast.eval_integral(*expr).map_err(|_| {
522 MaterialiseError::Unevaluated {
523 construct: "union case label".to_string(),
524 at: node.name.clone(),
525 }
526 })?),
527 CaseLabel::Default => is_default = true,
528 }
529 }
530 cases.push(UnionCase {
531 attribute: attribute(ast, member, &node.name)?,
532 labels,
533 is_default,
534 });
535 }
536 let mut attributes: Vec<Attribute> = cases
537 .iter()
538 .map(|c| Attribute {
539 name: c.attribute.name.clone(),
540 state: c.attribute.state.clone(),
541 type_: c.attribute.type_.clone(),
542 native: c.attribute.native,
543 })
544 .collect();
545 name_state(&mut attributes);
546
547 let used: Vec<i128> = cases.iter().flat_map(|c| c.labels.clone()).collect();
548 let unused = unused_discriminator(ast, disc, &used)?;
549 let has_default_case = cases.iter().any(|c| c.is_default);
550 let initial = if has_default_case {
553 unused
554 } else {
555 cases.iter().find_map(|c| c.labels.first().copied())
556 };
557 let initial_text = initial.map_or(String::new(), |v| {
558 format!("d_ = static_cast<{disc_type}>({v});")
559 });
560
561 let mut text = format!(
562 "/// `{}`: IDL4 to C++ 1.0 \u{a7}7.2.4.3.2. A modifier selects the case of its member.\nclass {name} {{\npublic:\n {name}() {{ {initial_text} }}\n {name}(const {name}&) = default;\n {name}({name}&&) = default;\n ~{name}() = default;\n {name}& operator=(const {name}&) = default;\n {name}& operator=({name}&&) = default;\n\n {disc_type} _d() const {{ return d_; }}\n void _d({disc_type} value) {{ d_ = value; }}\n\n",
563 node.name
564 );
565 for (attribute, case) in attributes.iter().zip(cases.iter()) {
566 let (n, st, ty) = (&attribute.name, &attribute.state, &attribute.type_);
567 let select_value =
570 case.labels
571 .first()
572 .copied()
573 .or(if case.is_default { unused } else { None });
574 let select = select_value.map_or(String::new(), |value| {
575 format!(" d_ = static_cast<{disc_type}>({value});")
576 });
577 let multiple = case.labels.len() > 1;
578 if attribute.native {
579 text.push_str(&format!(
580 " {ty} {n}() const {{ return {st}; }}\n {ty}& {n}() {{ return {st}; }}\n void {n}({ty} value) {{ {st} = value;{select} }}\n"
581 ));
582 if multiple {
583 text.push_str(&format!(
584 " void {n}({ty} value, {disc_type} discriminator) {{ {st} = value; d_ = discriminator; }}\n"
585 ));
586 }
587 } else {
588 text.push_str(&format!(
589 " const {ty}& {n}() const {{ return {st}; }}\n {ty}& {n}() {{ return {st}; }}\n void {n}(const {ty}& value) {{ {st} = value;{select} }}\n void {n}({ty}&& value) {{ {st} = std::move(value);{select} }}\n"
590 ));
591 if multiple {
592 text.push_str(&format!(
593 " void {n}(const {ty}& value, {disc_type} discriminator) {{ {st} = value; d_ = discriminator; }}\n void {n}({ty}&& value, {disc_type} discriminator) {{ {st} = std::move(value); d_ = discriminator; }}\n"
594 ));
595 }
596 }
597 }
598 if !has_default_case {
600 if let Some(value) = unused {
601 text.push_str(&format!(
602 "\n void _default() {{ d_ = static_cast<{disc_type}>({value}); }}\n"
603 ));
604 }
605 }
606 text.push_str(&format!(
607 "\n void swap({name}& other) {{\n using std::swap;\n swap(d_, other.d_);\n"
608 ));
609 for attribute in &attributes {
610 text.push_str(&format!(" swap({0}, other.{0});\n", attribute.state));
611 }
612 text.push_str(" }\n");
613 text.push_str(&format!(
614 "\n bool operator==(const {name}& other) const {{\n return d_ == other.d_"
615 ));
616 for attribute in &attributes {
617 text.push_str(&format!(" && {0} == other.{0}", attribute.state));
618 }
619 text.push_str(&format!(
620 ";\n }}\n bool operator!=(const {name}& other) const {{ return !(*this == other); }}\n\nprivate:\n {disc_type} d_{{}};\n"
621 ));
622 for attribute in &attributes {
623 text.push_str(&format!(
624 " {} {}{{}};\n",
625 attribute.type_, attribute.state
626 ));
627 }
628 text.push_str(&format!(
629 "}};\n\ninline void swap({name}& a, {name}& b) {{ a.swap(b); }}\n"
630 ));
631
632 let path = psm_path(ast, id);
633 let native = native_path(ast, id);
634 let mut conv = format!(
637 "template <>\nstruct Conv<{path}> {{\n using Native = {native};\n static Native to_native(const {path}& v) {{\n Native n;\n"
638 );
639 for case in &cases {
640 conv.push_str(&format!(
641 " n.{0}(Shape<std::remove_cv_t<std::remove_reference_t<decltype(n.{0}())>>, typename Conv<{1}>::Native>::apply(Conv<{1}>::to_native(v.{0}())));\n",
642 case.attribute.name, case.attribute.type_
643 ));
644 }
645 conv.push_str(&format!(
649 " n._d(Conv<{disc_type}>::to_native(v._d()));\n return n;\n }}\n static {path} from_native(const Native& n) {{\n {path} v;\n switch (static_cast<long long>(n._d())) {{\n"
650 ));
651 for case in &cases {
652 let line = format!(
653 " v.{0}(Conv<{1}>::from_native(Shape<typename Conv<{1}>::Native, std::remove_cv_t<std::remove_reference_t<decltype(n.{0}())>>>::apply(n.{0}())));\n break;\n",
654 case.attribute.name, case.attribute.type_
655 );
656 if case.is_default {
657 conv.push_str(" default:\n");
658 }
659 for label in &case.labels {
660 conv.push_str(&format!(" case {label}LL:\n"));
661 }
662 if !case.labels.is_empty() || case.is_default {
663 conv.push_str(&line);
664 }
665 }
666 if !has_default_case {
667 conv.push_str(" default:\n break;\n");
668 }
669 conv.push_str(&format!(
670 " }}\n v._d(Conv<{disc_type}>::from_native(n._d()));\n return v;\n }}\n}};\n"
671 ));
672 Ok((text, conv))
673}
674
675pub(crate) fn in_namespaces(chain: &[String], text: &str) -> String {
677 let mut out = String::new();
678 for name in chain {
679 out.push_str(&format!("namespace {name} {{\n"));
680 }
681 out.push_str(text);
682 if !text.ends_with('\n') {
683 out.push('\n');
684 }
685 for _ in chain {
686 out.push_str("}\n");
687 }
688 out
689}
690
691pub fn psm_layer(ast: &Ast) -> Result<String, MaterialiseError> {
697 let order = ordered_types(ast);
698 let pair = crate::emit::type_pair_cpp::has_pair_types(ast);
702 let runtime_defined = |node: &TypeNode| -> Result<bool, MaterialiseError> {
703 Ok(pair
704 && crate::emit::type_pair_cpp::is_runtime_defined(
705 &module_names(ast, node.module)?,
706 &node.name,
707 ))
708 };
709 let mut forward = String::new();
710 let mut types = String::new();
711 let mut conversions = String::new();
712
713 for id in &order {
714 let Ok(node) = ast.type_(*id) else {
715 return Err(no_form(
716 "a type the model would not hand over",
717 "forward declarations",
718 ));
719 };
720 if node.name.is_empty() || runtime_defined(node)? {
721 continue;
722 }
723 if matches!(
724 node.kind,
725 TypeKind::Structure { .. } | TypeKind::Union { .. }
726 ) {
727 let chain = module_names(ast, node.module)?;
728 forward.push_str(&in_namespaces(
729 &chain,
730 &format!("class {};", cxx_name(&node.name)),
731 ));
732 }
733 }
734 for id in order {
735 let Ok(node) = ast.type_(id) else {
736 return Err(no_form(
737 "a type the model would not hand over",
738 "type declarations",
739 ));
740 };
741 if node.name.is_empty() || runtime_defined(node)? {
742 continue;
743 }
744 let chain = module_names(ast, node.module)?;
745 let name = cxx_name(&node.name);
746 let (text, conv) = match &node.kind {
747 TypeKind::Structure { .. } => {
748 let (class, conv) = structure(ast, id, node)?;
749 (class, Some(conv))
750 }
751 TypeKind::Union { discriminator, .. } => {
752 let (class, conv) = union_class(ast, id, node, discriminator)?;
753 (class, Some(conv))
754 }
755 TypeKind::Enumeration { .. }
756 | TypeKind::Bitmask { .. }
757 | TypeKind::Bitset { .. }
758 | TypeKind::Native => (
759 format!(
760 "/// Declared once, with the IDL's name and constants (DDS-PSM-Cxx 1.0 \u{a7}8.1.3).\nusing {name} = {};\n",
761 native_path(ast, id)
762 ),
763 None,
764 ),
765 TypeKind::Alias { aliased } => {
766 let Some(target) = aliased.resolved() else {
767 return Err(no_form("an unresolved typedef", &node.name));
768 };
769 (
770 format!("using {name} = {};\n", psm_type(ast, target, &node.name)?),
771 None,
772 )
773 }
774 TypeKind::ValueBox { boxed } => {
775 let Some(target) = boxed.resolved() else {
776 return Err(no_form("an unresolved boxed value", &node.name));
777 };
778 (
779 format!(
780 "using {name} = std::shared_ptr<{}>;\n",
781 psm_type(ast, target, &node.name)?
782 ),
783 None,
784 )
785 }
786 TypeKind::Primitive(_)
789 | TypeKind::Fixed { .. }
790 | TypeKind::String { .. }
791 | TypeKind::Sequence { .. }
792 | TypeKind::Array { .. }
793 | TypeKind::Map { .. }
794 | TypeKind::Any
795 | TypeKind::AnnotationType { .. } => continue,
796 TypeKind::Valuetype { .. } | TypeKind::ValueBase | TypeKind::ObjectRef { .. } => {
799 return Err(no_form(
800 "a valuetype, interface or ValueBase has no DDS-PSM-Cxx 1.0 mapping",
801 &node.name,
802 ));
803 }
804 };
805 types.push_str(&in_namespaces(&chain, &text));
806 types.push('\n');
807 if let Some(conv) = conv {
808 conversions.push_str(&conv);
809 conversions.push('\n');
810 }
811 }
812 for (_, exception) in ast.exceptions() {
815 let chain = module_names(ast, exception.module)?;
816 let name = cxx_name(&exception.name);
817 let mut native = format!("::{NATIVE_NAMESPACE}");
818 for segment in &chain {
819 native.push_str("::");
820 native.push_str(segment);
821 }
822 native.push_str("::");
823 native.push_str(&name);
824 types.push_str(&in_namespaces(
825 &chain,
826 &format!("using {name} = {native};\n"),
827 ));
828 types.push('\n');
829 }
830 if types.is_empty() {
833 return Ok(String::new());
834 }
835 Ok(format!(
836 "// ---- The PSM layer: DDS-PSM-Cxx 1.0 \u{a7}8.1 over the native types above. ----\n\
837 // The native types are in `{NATIVE_NAMESPACE}`; these classes are what an application writes.\n\
838 // `zerodds::psm::Conv` hands them to the codec.\n\n{forward}\n{types}\
839 namespace zerodds {{\nnamespace psm {{\n\n{conversions}}} // namespace psm\n}} // namespace zerodds\n"
840 ))
841}
842
843#[cfg(test)]
844mod tests {
845 use super::*;
846
847 #[test]
848 fn a_name_that_is_an_accessor_is_not_a_state_member() {
849 let mut attributes = vec![
850 Attribute {
851 name: "a".into(),
852 state: String::new(),
853 type_: "int32_t".into(),
854 native: true,
855 },
856 Attribute {
857 name: "a_".into(),
858 state: String::new(),
859 type_: "int32_t".into(),
860 native: true,
861 },
862 ];
863 name_state(&mut attributes);
864 assert_eq!(attributes[0].state, "a__");
865 assert_eq!(attributes[1].state, "a___");
866 }
867
868 #[test]
869 fn the_primitives_are_the_table_7_1_names() {
870 assert_eq!(primitive(PrimitiveKind::WChar), "wchar_t");
871 assert_eq!(primitive(PrimitiveKind::Float128), "long double");
872 assert_eq!(primitive(PrimitiveKind::Octet), "uint8_t");
873 }
874}