zerodds_lang_rules/encode_c.rs
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! The C column's encoder: [`crate::emit::encode`] bound to the C
4//! backend.
5//!
6//! The walk moved to `emit/encode.rs` when it stopped being C's alone.
7//! What is left here is the binding, and it is a module rather than four
8//! turbofishes at each call site because the C generator asks for these
9//! by name in twenty places and a `::<C>` at each is twenty chances to
10//! reach for the wrong backend.
11//!
12//! What C forces that the walk does not decide:
13//!
14//! - **A pointer parameter.** A codec takes `const T *v` and reaches its
15//! members with `->`. Every target spells that its own way, and
16//! [`crate::emit::CodecTarget::member`] is where.
17//! - **An array parameter decays.** An array codec takes the array
18//! itself, not its address: C adjusts an array parameter to a pointer
19//! to its first element, and `(*v)[i][j]` would otherwise need
20//! parentheses the tree has no node for. So a call site passes
21//! `v->grid` for an array and `&v->x` for everything else.
22//! - **Prototypes ahead of bodies**, so a recursive or mutually
23//! recursive type does not depend on the order the walk visited its
24//! types in.
25
26use crate::emit::c::C;
27use crate::emit::encode;
28use crate::encode::EncodeError;
29use zerodds_ast::{Ast, TypeNode};
30use zerodds_ast_core::Id;
31use zerodds_ast_layout::key::{LayoutKey, Purpose};
32use zerodds_lang_cst::c;
33
34pub use crate::emit::encode::CodecOptions as CCodecOptions;
35
36/// Build the C encoder for every type in the model, under one plan.
37///
38/// # Errors
39/// As [`encode::codec`].
40pub fn c_codec(
41 ast: &Ast,
42 key: LayoutKey,
43 options: CCodecOptions,
44) -> Result<Vec<c::Decl>, EncodeError> {
45 encode::codec::<C>(ast, key, options)
46}
47
48/// The C encoder for the types one root reaches, and no others.
49///
50/// # Errors
51/// As [`encode::codec_from`].
52pub fn c_codec_from(
53 ast: &Ast,
54 root: Id<TypeNode>,
55 key: LayoutKey,
56 options: CCodecOptions,
57) -> Result<Vec<c::Decl>, EncodeError> {
58 encode::codec_from::<C>(ast, root, key, options)
59}
60
61/// The C key holder for a keyed type, and nothing for one with no `@key`.
62///
63/// # Errors
64/// As [`encode::key_holder`].
65pub fn c_key_holder(
66 ast: &Ast,
67 root: Id<TypeNode>,
68 key: LayoutKey,
69) -> Result<Option<c::Decl>, EncodeError> {
70 encode::key_holder::<C>(ast, root, key)
71}
72
73/// The name of the C encoder function for a type.
74///
75/// zerodds-lint: recursion-depth 1 — this does not recurse. The callee
76/// is `encode::codec_name::<C>`; `dds_bounded_recursion` compares only the last
77/// segment of a call path, so it reads as a self-call.
78#[must_use]
79pub fn codec_name(ast: &Ast, id: Id<TypeNode>, purpose: Purpose) -> String {
80 encode::codec_name::<C>(ast, id, purpose)
81}
82
83/// Whether the C codec generated for `id` may legitimately have no
84/// caller. See `encode::maybe_unused`.
85///
86/// zerodds-lint: recursion-depth 1 — this does not recurse. The callee
87/// is `encode::maybe_unused`; `dds_bounded_recursion` compares only the last
88/// segment of a call path, so it reads as a self-call.
89#[must_use]
90pub fn maybe_unused(ast: &Ast, id: Id<TypeNode>, key: LayoutKey) -> bool {
91 encode::maybe_unused(ast, id, key)
92}