zerodds_wire_codec/sink.rs
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! What the read direction builds a value into.
4//!
5//! The counterpart of [`crate::source`], and the asymmetry between them
6//! is real: reading a value needs accessors, building one needs
7//! constructors, and a constructor has to be told *what kind of thing*
8//! it is building or the representation cannot store it.
9//!
10//! ## Every constructor carries its type
11//!
12//! [`Sink::integer`], [`Sink::float`] and [`Sink::character`] all take
13//! the [`PrimitiveKind`] the value came from, and none of them has to.
14//! `serde_json::Value` uses it for exactly one decision — a 64-bit width
15//! becomes a decimal string, because a JSON number is a double and loses
16//! the low bits past 2^53 — and could have made that decision from the
17//! magnitude instead. It does not, because a form that changed shape at
18//! 2^53 would compare unequal across the boundary, and because a
19//! representation that stores an `int16` differently from an `int32`
20//! must be *told* which it has rather than left to infer it from a value
21//! that fits both.
22//!
23//! This is the hazard the module note in [`crate`] names, in its
24//! constructor form: a coordinate the current implementation happens not
25//! to need is still a coordinate, and dropping it from the signature is
26//! what makes the next implementation guess.
27
28use crate::encode::ProbeError;
29use std::string::String;
30use std::vec::Vec;
31use zerodds_ast::PrimitiveKind;
32use zerodds_float128::Float128;
33
34/// A value the read direction can be built into.
35///
36/// `Clone` because Table 9's default for an array is "the same
37/// dimensions and same element type whose elements take the default
38/// value for their corresponding type": the default of a type does not
39/// depend on the index, so one element is built and cloned rather than
40/// the model walked once per element — a 4096-element array of a nested
41/// structure would otherwise be 4096 identical walks.
42pub trait Sink: Sized + Clone {
43 /// `true` or `false`.
44 #[must_use]
45 fn boolean(value: bool) -> Self;
46
47 /// An integer of `kind`.
48 ///
49 /// `value` is already inside `kind`'s range: the reader took it out
50 /// of a field of that width, and [`crate::canonical`] put it through
51 /// [`crate::contract::integer_for`] first.
52 ///
53 /// # Errors
54 /// [`ProbeError::Mismatch`] where the representation cannot carry
55 /// the value, naming `path`.
56 fn integer(kind: PrimitiveKind, value: i128, path: &str) -> Result<Self, ProbeError>;
57
58 /// A floating value of `kind`.
59 ///
60 /// # Errors
61 /// [`ProbeError::Mismatch`] where the representation cannot write
62 /// the value — JSON has no NaN and no infinity — naming `path`.
63 fn float(kind: PrimitiveKind, value: f64, path: &str) -> Result<Self, ProbeError>;
64
65 /// A `long double`, exactly.
66 #[must_use]
67 fn float128(value: Float128) -> Self;
68
69 /// One character of `kind`.
70 #[must_use]
71 fn character(kind: PrimitiveKind, value: char) -> Self;
72
73 /// A `string` or a `wstring`.
74 ///
75 /// Also the loose form of a `@default` literal that has not met its
76 /// type yet — an enumerator's name, a character, a decimal integer —
77 /// which [`crate::canonical::canonicalise`] then reads back through
78 /// [`crate::source::Source`] and rebuilds at the member's own type.
79 /// That round trip is why the loose form has to be readable and not
80 /// only storable.
81 #[must_use]
82 fn text(value: String) -> Self;
83
84 /// A `fixed<P, S>`, in the canonical decimal
85 /// `crate::canonical::decimal` gives it.
86 #[must_use]
87 fn fixed(decimal: String) -> Self;
88
89 /// The enumerator this value names.
90 ///
91 /// A name and never the number behind it: the number depends on
92 /// `@value` and on the dialect's
93 /// [`zerodds_ast_layout::EnumeratorValue`], and two peers that
94 /// number their enumerators differently still mean the same
95 /// enumerator.
96 #[must_use]
97 fn enumerator(name: String) -> Self;
98
99 /// A bitmask, as the names of the bits it sets, in declaration
100 /// order.
101 #[must_use]
102 fn bit_names(names: Vec<String>) -> Self;
103
104 /// A bitset, as its named bitfields in declaration order.
105 ///
106 /// Anonymous bitfields are padding and are not among them.
107 ///
108 /// # Errors
109 /// [`ProbeError::Mismatch`] where the representation cannot carry a
110 /// field's value, naming `path`.
111 fn bitfields(fields: Vec<(String, i128)>, path: &str) -> Result<Self, ProbeError>;
112
113 /// A sequence, an array or a map.
114 #[must_use]
115 fn collection(items: Vec<Self>) -> Self;
116
117 /// One entry of a map, as a key and a value.
118 ///
119 /// A pair and not a named field: a `map<long, float>` keys on
120 /// something that is not a name, which §7.4.1.1.4 admits and a
121 /// field-keyed form does not. The pair has to read back out through
122 /// [`crate::source::Source::as_items`] in that order, because that
123 /// is how a map's entries are put into one order for comparison.
124 #[must_use]
125 fn map_entry(key: Self, value: Self) -> Self;
126
127 /// A structure, a valuetype or a union.
128 #[must_use]
129 fn aggregate(members: Aggregate<Self>) -> Self;
130
131 /// A number no type governs — the value of a `@default` float
132 /// literal, before the member's own width narrows it.
133 ///
134 /// # Errors
135 /// [`ProbeError::Mismatch`] where the representation cannot write
136 /// the value, naming `path`.
137 fn number(value: f64, path: &str) -> Result<Self, ProbeError>;
138}
139
140/// An aggregate under construction.
141///
142/// The read direction does not build a structure in one go: a
143/// `@mutable` type is driven by the headers the writer wrote, in
144/// whatever order it wrote them, and the members the stream left out
145/// take their defaults afterwards. So the value is assembled by name,
146/// and the two things the assembly has to answer are whether a member
147/// is already in — §7.4.1.2.3's defaulting asks it — and whether one
148/// arrived twice, which is a stream this type does not describe.
149///
150/// Insertion order is preserved and a repeat replaces in place, which
151/// is `serde_json::Map::insert`'s contract and the one the two
152/// `@mutable` readers were written against.
153pub struct Aggregate<V> {
154 entries: Vec<(String, V)>,
155}
156
157impl<V> Default for Aggregate<V> {
158 fn default() -> Self {
159 Self::new()
160 }
161}
162
163impl<V> Aggregate<V> {
164 /// A fresh, empty aggregate.
165 #[must_use]
166 pub const fn new() -> Self {
167 Self {
168 entries: Vec::new(),
169 }
170 }
171
172 /// Whether a member of this name is already in.
173 #[must_use]
174 pub fn contains(&self, name: &str) -> bool {
175 self.entries.iter().any(|(known, _)| known == name)
176 }
177
178 /// Put a member in, answering with the one it replaced.
179 ///
180 /// `Some` is what says a stream carried the same member twice.
181 pub fn insert(&mut self, name: String, value: V) -> Option<V> {
182 for (known, held) in &mut self.entries {
183 if *known == name {
184 return Some(core::mem::replace(held, value));
185 }
186 }
187 self.entries.push((name, value));
188 None
189 }
190
191 /// The members, in the order they went in.
192 #[must_use]
193 pub fn into_entries(self) -> Vec<(String, V)> {
194 self.entries
195 }
196}