Skip to main content

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}