Skip to main content

zerodds_lang_rules/
decode_c.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 walk moved to `emit/decode.rs` when it stopped being C's alone,
7//! the same as the encoder's did. What is left here is the binding, and
8//! the one step the generic walk cannot take: prepending the Table 9
9//! defaulters.
10//!
11//! ## Why the defaulters are here and not in the walk
12//!
13//! A member the stream did not carry takes Table 9's value, and in C
14//! that means a generated function per type: a struct-valued default is
15//! not an expression C can assign. Which types need one is read off the
16//! decoders that were generated — `c_defaulters_called_by` looks at
17//! them — and a target that writes its defaults as literals needs no
18//! such pass at all. So the pass belongs to the column, not to the walk.
19
20use crate::emit::c::C;
21use crate::emit::decode;
22use crate::encode::EncodeError;
23use zerodds_ast::{Ast, TypeNode};
24use zerodds_ast_core::Id;
25use zerodds_ast_layout::key::LayoutKey;
26use zerodds_lang_cst::c;
27
28pub use crate::emit::encode::CodecOptions as CCodecOptions;
29
30/// The name of the C decoder function for a type.
31///
32/// zerodds-lint: recursion-depth 1 — this does not recurse. The callee
33/// is `decode::decoder_name::<C>`; `dds_bounded_recursion` compares only the last
34/// segment of a call path, so it reads as a self-call.
35#[must_use]
36pub fn decoder_name(ast: &Ast, id: Id<TypeNode>) -> String {
37    decode::decoder_name::<C>(ast, id)
38}
39
40/// Build the C decoder for every type in the model, under one plan.
41///
42/// # Errors
43/// As [`decode::decoder`], and when a Table 9 defaulter cannot be built.
44pub fn c_decoder(
45    ast: &Ast,
46    key: LayoutKey,
47    options: CCodecOptions,
48) -> Result<Vec<c::Decl>, EncodeError> {
49    with_defaulters(ast, decode::decoder::<C>(ast, key, options)?)
50}
51
52/// The C decoder for the types one root reaches, and no others.
53///
54/// # Errors
55/// As [`c_decoder`].
56pub fn c_decoder_from(
57    ast: &Ast,
58    root: Id<TypeNode>,
59    key: LayoutKey,
60    options: CCodecOptions,
61) -> Result<Vec<c::Decl>, EncodeError> {
62    with_defaulters(ast, decode::decoder_from::<C>(ast, root, key, options)?)
63}
64
65/// The Table 9 defaulters, ahead of the decoders that call them.
66///
67/// `c_defaulters_called_by` emits one only for a type some member can
68/// actually go missing at, so nothing it returns is a `static` function
69/// with no caller — which the probe compiles as an error.
70fn with_defaulters(ast: &Ast, decoders: Vec<c::Decl>) -> Result<Vec<c::Decl>, EncodeError> {
71    let mut out = crate::default_c::c_defaulters_called_by(ast, &decoders)?;
72    out.extend(decoders);
73    Ok(out)
74}