zerodds_lang_rules/decode_cpp.rs
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! The C++ column's decoder: [`crate::emit::decode`] bound to the C++
4//! backend.
5//!
6//! The counterpart of [`crate::decode_c`], and the same shape: the walk
7//! is shared and what is left here is the binding plus the one step the
8//! generic walk cannot take — prepending the Table 9 defaulters.
9//!
10//! ## Why the defaulters are here and not in the walk
11//!
12//! Which types need one is read off the decoders that were generated,
13//! and a target that wrote its defaults as literals would need no such
14//! pass at all. So the pass belongs to the column. C++ needs it for the
15//! same reason C does, and `crate::default_cpp` says why it is not
16//! `T{}`.
17
18use crate::emit::cpp::Cpp;
19use crate::emit::decode;
20use crate::encode::EncodeError;
21use zerodds_ast::{Ast, TypeNode};
22use zerodds_ast_core::Id;
23use zerodds_ast_layout::key::LayoutKey;
24use zerodds_lang_cst::cpp;
25
26pub use crate::emit::encode::CodecOptions as CppCodecOptions;
27
28/// The name of the C++ decoder function for a type.
29///
30/// zerodds-lint: recursion-depth 1 — this does not recurse. The callee
31/// is `decode::decoder_name::<Cpp>`; `dds_bounded_recursion` compares only the last
32/// segment of a call path, so it reads as a self-call.
33#[must_use]
34pub fn decoder_name(ast: &Ast, id: Id<TypeNode>) -> String {
35 decode::decoder_name::<Cpp>(ast, id)
36}
37
38/// Build the C++ decoder for every type in the model, under one plan.
39///
40/// # Errors
41/// As [`decode::decoder`], and when a Table 9 defaulter cannot be built.
42pub fn cpp_decoder(
43 ast: &Ast,
44 key: LayoutKey,
45 options: CppCodecOptions,
46) -> Result<Vec<cpp::Decl>, EncodeError> {
47 with_defaulters(ast, decode::decoder::<Cpp>(ast, key, options)?)
48}
49
50/// The C++ decoder for the types one root reaches, and no others.
51///
52/// # Errors
53/// As [`cpp_decoder`].
54pub fn cpp_decoder_from(
55 ast: &Ast,
56 root: Id<TypeNode>,
57 key: LayoutKey,
58 options: CppCodecOptions,
59) -> Result<Vec<cpp::Decl>, EncodeError> {
60 with_defaulters(ast, decode::decoder_from::<Cpp>(ast, root, key, options)?)
61}
62
63/// The C++ decoder for the types several roots reach, each function once.
64///
65/// # Errors
66/// As [`cpp_decoder`].
67pub fn cpp_decoder_from_many(
68 ast: &Ast,
69 roots: &[Id<TypeNode>],
70 key: LayoutKey,
71 options: CppCodecOptions,
72) -> Result<Vec<cpp::Decl>, EncodeError> {
73 with_defaulters(
74 ast,
75 decode::decoder_from_many::<Cpp>(ast, roots, key, options)?,
76 )
77}
78
79/// [`cpp_decoder_from_many`] without the types in `excluded`.
80///
81/// # Errors
82/// As [`cpp_decoder`].
83pub fn cpp_decoder_from_many_excluding(
84 ast: &Ast,
85 roots: &[Id<TypeNode>],
86 key: LayoutKey,
87 options: CppCodecOptions,
88 excluded: &[Id<TypeNode>],
89) -> Result<Vec<cpp::Decl>, EncodeError> {
90 let decoders = decode::decoder_from_many_excluding::<Cpp>(ast, roots, key, options, excluded)?;
91 let mut out = crate::default_cpp::cpp_defaulters_called_by_excluding(ast, &decoders, excluded)?;
92 out.extend(decoders);
93 Ok(out)
94}
95
96/// The Table 9 defaulters, ahead of the decoders that call them.
97///
98/// `cpp_defaulters_called_by` emits one only for a type a value can
99/// actually go missing at, so nothing it returns is a `static` function
100/// with no caller — which the probe compiles as an error under
101/// `-Wunused-function -Werror`.
102fn with_defaulters(ast: &Ast, decoders: Vec<cpp::Decl>) -> Result<Vec<cpp::Decl>, EncodeError> {
103 let mut out = crate::default_cpp::cpp_defaulters_called_by(ast, &decoders)?;
104 out.extend(decoders);
105 Ok(out)
106}