msgpack_tagged/deserializer.rs
1//! Tagged-map msgpack deserializer that wraps [`rmp_serde::Deserializer`].
2//!
3//! Mirrors [`crate::serializer::Serializer`]: each aggregate shape that the
4//! macro emits — named struct, multi-element tuple struct, sequence, tuple,
5//! map, option, newtype struct — is intercepted to translate integer wire
6//! tags back to the serde field/variant names the [`Visitor`] expects, via
7//! the [`TagRegistry`].
8//!
9//! The public entry point is [`msgpack_tagged_deserialize`], which builds
10//! the registry up front via `T::register_into` and runs the bytes through
11//! the wrapper.
12//!
13//! ## Known gaps vs. the design doc / macro syntax
14//!
15//! The wrapper isn't final — the bits below are accepted by
16//! `#[derive(MsgpackTagged)]` today but the deserializer doesn't model
17//! them yet.
18//!
19//! - **`deserialize_any`.** Niche today (none of our ACIR types are
20//! decoded via self-describing visitors), but nested tagged values
21//! reached through `serde_json::Value`-style consumers wouldn't
22//! recurse through this wrapper.
23//! - **Encoding strategies.** Only the **Tagged** strategy is decoded.
24//! When the serializer gains **Array** / **Named** overrides, the
25//! deserializer needs the matching shape-agnostic dispatch (peek
26//! marker, route to the right reader).
27
28use rmp::Marker;
29use rmp_serde::Deserializer as RmpDeserializer;
30use rmp_serde::decode::ReadReader;
31// `de::Deserializer` would clash with our own `Deserializer` struct below if
32// pulled in via `use`; importing the `de` module instead lets us write
33// `de::Deserializer` for the trait at the few sites that need it.
34use serde::de::{
35 self, Deserialize, DeserializeSeed, EnumAccess, Error as _, MapAccess, SeqAccess,
36 VariantAccess, Visitor,
37};
38use smallvec::SmallVec;
39
40use crate::{MsgpackTagged, TagRegistry};
41
42/// `rmp_serde`'s decode-side error type, re-exported for our `Deserializer`
43/// impl. Matches the encode-side `RmpError` re-export in `serializer.rs`.
44type RmpError = rmp_serde::decode::Error;
45
46/// Buffered `(wire_tag, value-bytes)` pairs from an int-keyed map. Inline
47/// capacity 4 covers the typical tuple-struct / tuple-variant arity without
48/// a heap allocation; anything larger transparently spills.
49type IntKeyedEntries<'de> = SmallVec<[(u8, &'de [u8]); 4]>;
50
51/// Tagged-map msgpack deserializer.
52///
53/// Constructed internally by [`msgpack_tagged_deserialize`]. Exposed as `pub`
54/// so it can be named from rustdoc; the only stable entry point is still the
55/// free function. A builder for strategy customization will land later
56/// (mirroring the serializer's plan).
57///
58/// The inner reader is fixed to `&'de [u8]` (wrapped in rmp_serde's
59/// `ReadReader`, the shape `RmpDeserializer::new` constructs). We don't
60/// keep the reader generic because two interception paths need to grab a
61/// snapshot of the unread byte slice — `deserialize_option`'s peek-via-
62/// rewind dance and `deserialize_tuple_struct`'s buffer-by-tag — and
63/// those slice tricks only work when the reader exposes the underlying
64/// `&[u8]` directly. The public entry function only ever constructs the
65/// `&[u8]` shape, so this isn't a real loss of generality; if/when a
66/// `from_read` variant lands, that's a separate type.
67pub struct Deserializer<'a, 'de> {
68 inner: RmpDeserializer<ReadReader<&'de [u8]>>,
69 registry: &'a TagRegistry,
70}
71
72impl<'a, 'de> Deserializer<'a, 'de> {
73 /// Construct a deserializer over `bytes` borrowing the caller-built
74 /// `registry`. Symmetric with [`crate::Serializer::new`].
75 pub fn new(bytes: &'de [u8], registry: &'a TagRegistry) -> Self {
76 Self { inner: RmpDeserializer::new(bytes), registry }
77 }
78
79 /// Read a msgpack map header (`fixmap` / `map16` / `map32`) and return
80 /// its length as `usize`. Thin wrapper over `rmp::decode::read_map_len`
81 /// that bakes in our error-formatting + the `u32 → usize` cast every
82 /// caller wants.
83 fn read_map_len(&mut self) -> Result<usize, RmpError> {
84 rmp::decode::read_map_len(self.inner.get_mut())
85 .map_err(|e| RmpError::custom(format!("failed to read msgpack map length: {e:?}")))
86 .map(|n| n as usize)
87 }
88
89 /// Read a msgpack array header (`fixarray` / `array16` / `array32`)
90 /// and return its length as `usize`. Sibling of [`Self::read_map_len`].
91 fn read_array_len(&mut self) -> Result<usize, RmpError> {
92 rmp::decode::read_array_len(self.inner.get_mut())
93 .map_err(|e| RmpError::custom(format!("failed to read msgpack array length: {e:?}")))
94 .map(|n| n as usize)
95 }
96}
97
98/// Build the tag registry from `T::register_into`, then deserialize a value
99/// of type `T` from `bytes` through a [`Deserializer`].
100///
101/// All tagged types are expected to be encoded under `Format::MsgpackTagged`
102/// (int-keyed maps for [`crate::EncodingStrategy::Tagged`], positional
103/// arrays for [`crate::EncodingStrategy::Array`] — the decoder probes the
104/// wire shape per struct). Other formats route through their own decoders
105/// upstream via the `Format` byte (defined in the `acir` crate).
106pub fn msgpack_tagged_deserialize<'de, T>(bytes: &'de [u8]) -> std::io::Result<T>
107where
108 T: Deserialize<'de> + MsgpackTagged,
109{
110 let registry = TagRegistry::from_type::<T>();
111 let mut deserializer = Deserializer::new(bytes, ®istry);
112 T::deserialize(&mut deserializer).map_err(std::io::Error::other)
113}
114
115// ============================================================================
116// `serde::Deserializer` impl — every method currently forwards to the inner
117// rmp_serde deserializer. The structurally-significant ones (struct, enum,
118// tuple shapes, sequences, maps) need interception so integer wire tags are
119// translated back to serde field/variant names.
120// ============================================================================
121
122impl<'a, 'de> de::Deserializer<'de> for &mut Deserializer<'a, 'de> {
123 type Error = RmpError;
124
125 // TODO: when used with self-describing visitors (e.g. `serde_json::Value`,
126 // `untagged` enums), `deserialize_any` peeks at the next msgpack token
127 // and dispatches. rmp_serde routes nested values through its inner
128 // deserializer, so a tagged value reachable from a self-describing
129 // consumer wouldn't recurse via this wrapper. Niche today — none of our
130 // ACIR types are decoded via `deserialize_any` — but flag for parity.
131 fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
132 (&mut self.inner).deserialize_any(visitor)
133 }
134
135 // -------- primitive scalars: forward verbatim ---------------------------
136
137 fn deserialize_bool<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
138 (&mut self.inner).deserialize_bool(visitor)
139 }
140
141 fn deserialize_i8<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
142 (&mut self.inner).deserialize_i8(visitor)
143 }
144 fn deserialize_i16<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
145 (&mut self.inner).deserialize_i16(visitor)
146 }
147 fn deserialize_i32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
148 (&mut self.inner).deserialize_i32(visitor)
149 }
150 fn deserialize_i64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
151 (&mut self.inner).deserialize_i64(visitor)
152 }
153 fn deserialize_i128<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
154 (&mut self.inner).deserialize_i128(visitor)
155 }
156
157 fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
158 (&mut self.inner).deserialize_u8(visitor)
159 }
160 fn deserialize_u16<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
161 (&mut self.inner).deserialize_u16(visitor)
162 }
163 fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
164 (&mut self.inner).deserialize_u32(visitor)
165 }
166 fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
167 (&mut self.inner).deserialize_u64(visitor)
168 }
169 fn deserialize_u128<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
170 (&mut self.inner).deserialize_u128(visitor)
171 }
172
173 fn deserialize_f32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
174 (&mut self.inner).deserialize_f32(visitor)
175 }
176 fn deserialize_f64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
177 (&mut self.inner).deserialize_f64(visitor)
178 }
179
180 fn deserialize_char<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
181 (&mut self.inner).deserialize_char(visitor)
182 }
183 fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
184 (&mut self.inner).deserialize_str(visitor)
185 }
186 fn deserialize_string<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
187 (&mut self.inner).deserialize_string(visitor)
188 }
189 fn deserialize_bytes<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
190 (&mut self.inner).deserialize_bytes(visitor)
191 }
192 fn deserialize_byte_buf<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
193 (&mut self.inner).deserialize_byte_buf(visitor)
194 }
195
196 fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
197 // We need to peek at the next msgpack marker to decide nil → None
198 // vs. anything else → Some. rmp_serde keeps an internal marker
199 // buffer for this dance, but it's private — so we read the marker
200 // via `rmp::decode::read_marker` directly against the underlying
201 // reader, and on a non-nil marker we restore the reader's state so
202 // the inner deserializer re-reads the marker as part of the value.
203 //
204 // `R: Clone` is the cost of admission for that restore. For the
205 // `&[u8]`-shaped readers that the public entry function constructs
206 // it's a trivial slice-handle copy.
207 let reader_state_before: &'de [u8] = self.inner.get_ref();
208 let marker = rmp::decode::read_marker(self.inner.get_mut())
209 .map_err(|e| RmpError::custom(format!("failed to read msgpack marker: {e:?}")))?;
210 if matches!(marker, Marker::Null) {
211 visitor.visit_none()
212 } else {
213 // Restoring is load-bearing, not just hygiene: a msgpack
214 // value's marker byte IS the first byte of the value
215 // (`Some(42u32)` → `0x2a` alone; `Some(255u32)` → `0xcc 0xff`).
216 // Leaving the reader past the marker would have the inner
217 // deserializer's `read_marker` either misinterpret the
218 // payload as a new marker or hit EOF, depending on the
219 // value's shape. rmp_serde's own buffered-marker trick (its
220 // private `marker: Option<Marker>` field) is unreachable from
221 // here, so restore-then-recurse is the cleanest available
222 // option. After the restore, `visit_some(&mut *self)`
223 // recurses through this wrapper so nested tagged types inside
224 // the inner value still get our interception.
225 *self.inner.get_mut() = reader_state_before;
226 visitor.visit_some(&mut *self)
227 }
228 }
229
230 fn deserialize_unit<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
231 // Lenient on purpose: consume whatever single msgpack value sits at
232 // this position and hand `()` to the visitor. Plain serde requires
233 // the wire to carry `nil` to satisfy a `()` field; our wrapper
234 // relaxes that so a `()` field can act as a portable "skip this
235 // position" placeholder.
236 //
237 // Use case: a stripped-down DTO that wants to ignore part of the
238 // wire while keeping the surrounding tag layout intact. The
239 // canonical example is `ProgramWithoutBrillig` in
240 // `acvm-repo/acir/src/lib.rs` — same tag layout as `Program`,
241 // but `unconstrained_functions: ()` discards the brillig payload
242 // so the C++ codegen consumer can read just the ACIR section.
243 // The C++ side achieves the same effect with `std::monostate`;
244 // this hook keeps Rust-side decoding symmetric.
245 //
246 // Unit-variant payloads (encoded as a literal `nil`) flow through
247 // here too and still work — `IgnoredAny` reads the nil byte and
248 // we visit `()` exactly as before.
249 de::IgnoredAny::deserialize(&mut *self)?;
250 visitor.visit_unit()
251 }
252
253 fn deserialize_unit_struct<V: Visitor<'de>>(
254 self,
255 name: &'static str,
256 visitor: V,
257 ) -> Result<V::Value, Self::Error> {
258 // No fields → no tag table to consult; passthrough is fine.
259 (&mut self.inner).deserialize_unit_struct(name, visitor)
260 }
261
262 fn deserialize_newtype_struct<V: Visitor<'de>>(
263 self,
264 name: &'static str,
265 visitor: V,
266 ) -> Result<V::Value, Self::Error> {
267 // rmp_serde handles its `_ExtStruct` newtype specially (msgpack
268 // extension types — timestamps and friends), reading an ext header
269 // and constructing an `ExtDeserializer`. We don't model that wire
270 // shape here, so let it through to inner verbatim. Every other
271 // newtype passes through to the inner type on the wire (matching
272 // the serializer's `serialize_newtype_struct`) — call
273 // `visitor.visit_newtype_struct(&mut *self)` so the inner value's
274 // deserialization recurses through this wrapper.
275 if name == rmp_serde::MSGPACK_EXT_STRUCT_NAME {
276 return (&mut self.inner).deserialize_newtype_struct(name, visitor);
277 }
278 visitor.visit_newtype_struct(&mut *self)
279 }
280
281 // -------- collection / aggregate shapes
282
283 fn deserialize_seq<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
284 // Read the msgpack array header (length-prefixed; `read_array_len`
285 // consumes the marker and returns the element count). Unlike the
286 // option case, the marker is metadata — the elements come AFTER
287 // it — so consuming it is correct and we don't need to restore.
288 // The adapter then yields each element via `&mut *self.parent`
289 // so any tagged element inside the sequence recurses through
290 // this wrapper.
291 //
292 // Byte-shaped values (`Vec<u8>`-equivalents like `FieldElement`)
293 // arrive via `deserialize_bytes`, not `deserialize_seq` — see
294 // the comment on `FieldElement::serialize` for why the encode
295 // side uses `serialize_bytes` directly. So `deserialize_seq`
296 // doesn't need to peek for `bin` markers here.
297 let len = self.read_array_len()?;
298 visitor.visit_seq(TaggedAccessViaParent { parent: self, remaining: len })
299 }
300
301 fn deserialize_tuple<V: Visitor<'de>>(
302 self,
303 len: usize,
304 visitor: V,
305 ) -> Result<V::Value, Self::Error> {
306 // Same wire shape as `deserialize_seq` — a length-prefixed msgpack
307 // array — so we share the access adapter. The added eager check
308 // is that the wire length matches the tuple arity: serde's tuple
309 // visitor reads exactly `len` elements and stops, so a wire that
310 // claims more would silently leave trailing bytes in the stream
311 // and corrupt subsequent reads.
312 let actual = self.read_array_len()?;
313 if actual != len {
314 return Err(RmpError::custom(format!(
315 "tuple length mismatch: type expects {len} elements, wire has {actual}",
316 )));
317 }
318 visitor.visit_seq(TaggedAccessViaParent { parent: self, remaining: len })
319 }
320
321 fn deserialize_tuple_struct<V: Visitor<'de>>(
322 self,
323 name: &'static str,
324 // Unused: per-position dispatch is driven by `TaggedTupleStructAccess`
325 // (which knows the arity via the visitor itself). Tag-set validation
326 // is in `buffer_and_validate_int_keyed_map`.
327 _len: usize,
328 visitor: V,
329 ) -> Result<V::Value, Self::Error> {
330 // Top-level tuple structs (`struct Pair(u32, bool)`) come in two
331 // wire shapes:
332 // * `Map` (Tagged): int-keyed `{tag → value}` pairs. Each
333 // entry's tag is read from the wire; we buffer
334 // `(tag, value-bytes)` and dispatch by tag in
335 // `next_element_seed` so reconstruction is robust to whatever
336 // order the serializer emits entries in.
337 // * `Array` (positional): bare `[v0, v1, …]`. Each entry's tag
338 // is synthesized from `product.fields[wire_pos]` (the wire is
339 // in tag-ascending order). The downstream
340 // `TaggedTupleStructAccess` dispatch is identical to the Map
341 // case once the buffer is materialized.
342 let context = || format!("tuple-struct {name:?}");
343 let product = self.product_for(name);
344 let entries = match self.peek_wire_shape()? {
345 WireShape::Map => {
346 let wire_len = self.read_map_len()?;
347 self.buffer_and_validate_int_keyed_map(wire_len, &product, context)?
348 }
349 WireShape::Array => {
350 let wire_len = self.read_array_len()?;
351 self.buffer_positional_array(wire_len, &product, context)?
352 }
353 };
354 visitor.visit_seq(TaggedTupleStructAccess {
355 parent: self,
356 product,
357 entries,
358 next_position: 0,
359 })
360 }
361
362 fn deserialize_map<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
363 // Same shape as `deserialize_seq` — read the length-prefixed
364 // header, then yield each entry's key+value through the parent.
365 let len = self.read_map_len()?;
366 visitor.visit_map(TaggedAccessViaParent { parent: self, remaining: len })
367 }
368
369 fn deserialize_struct<V: Visitor<'de>>(
370 self,
371 name: &'static str,
372 _fields: &'static [&'static str],
373 visitor: V,
374 ) -> Result<V::Value, Self::Error> {
375 // Look up the type's `Product` in the registry — it carries the
376 // tag ↔ field-name mapping we need to feed the visitor — then peek
377 // the wire shape to dispatch:
378 // * `Map` → existing `TaggedProductMapAccess`, reading tags from
379 // the wire and translating to field names.
380 // * `Array` → `ArrayProductMapAccess`, walking `product.fields`
381 // positionally and synthesizing the field-name keys.
382 // In either case the visitor receives `(field_name, value)` pairs
383 // via `visit_map` — that's how serde-derive's `Deserialize` for
384 // named structs is driven — and serde-derive's `#[serde(default)]`
385 // machinery covers any missing trailing positions on short wires.
386 let product = self.product_for(name);
387 match self.peek_wire_shape()? {
388 WireShape::Map => {
389 let len = self.read_map_len()?;
390 visitor.visit_map(TaggedProductMapAccess {
391 parent: self,
392 product,
393 type_name: name,
394 remaining: len,
395 })
396 }
397 WireShape::Array => {
398 let wire_len = self.read_array_len()?;
399 let layout = merged_wire_layout(&product);
400 // Cap check: extras past the merged (active + reserved)
401 // layout are only allowed when `allow_unknown_tags` is set.
402 // Reserved slots inside the cap are drained silently inside
403 // `ArrayProductMapAccess`.
404 if !product.allow_unknown_tags && wire_len > layout.len() {
405 return Err(RmpError::custom(format!(
406 "struct {name:?}: wire has {wire_len} positional entries but the \
407 type accepts at most {} under Array strategy ({} active + {} \
408 reserved); opt into `#[tagged(allow_unknown_tags)]` to \
409 silently skip extras",
410 layout.len(),
411 product.fields.len(),
412 product.reserved.len(),
413 )));
414 }
415 visitor.visit_map(ArrayProductMapAccess {
416 parent: self,
417 product,
418 layout,
419 wire_remaining: wire_len,
420 next_position: 0,
421 })
422 }
423 }
424 }
425
426 // Outer wire shape: a 1-entry msgpack map `{u8 variant_tag: payload}`,
427 // mirroring `Serializer::write_variant_header`. We read the map header
428 // and the variant tag here, then hand off to `TaggedEnumAccess` which
429 // (a) yields the variant *name* to the visitor via
430 // `BorrowedStrDeserializer` and (b) implements `VariantAccess` by
431 // dispatching on the registered variant's `VariantKind`. Payload
432 // values route back through `&mut self.parent` so nested tagged types
433 // recurse through this wrapper.
434 //
435 // Lenient handling of unknown / reserved variant tags is not modeled
436 // yet — see the file-level note about `allow_unknown` / `allow_reserved`.
437 fn deserialize_enum<V: Visitor<'de>>(
438 self,
439 name: &'static str,
440 _variants: &'static [&'static str],
441 visitor: V,
442 ) -> Result<V::Value, Self::Error> {
443 let sum = self.sum_for(name);
444 let outer_len = self.read_map_len()?;
445 if outer_len != 1 {
446 return Err(RmpError::custom(format!(
447 "MsgpackTagged: enum {name:?} expects a 1-entry outer map, got {outer_len}",
448 )));
449 }
450 let tag: u8 = u8::deserialize(&mut *self)?;
451 if let Some(variant) = sum.variants.iter().find(|v| v.tag == tag).copied() {
452 return visitor.visit_enum(TaggedEnumAccess {
453 parent: self,
454 variant,
455 payload_already_consumed: false,
456 });
457 }
458 // Unknown wire tag. Strict per-marker routing:
459 // * tag in `sum.reserved` → only `on_reserved_tag` catches it
460 // * tag in neither variants nor reserved → only `on_unknown_tag`
461 // * otherwise → error
462 // The two cases are intentionally non-overlapping: a user who wants
463 // unified handling puts both `#[tagged(on_reserved)]` and
464 // `#[tagged(on_unknown)]` on a single variant, in which case both
465 // `Sum` fields point at the same tag and either path lands there.
466 // A user who marks only one is making a deliberate "this kind of
467 // drift is tolerable, that kind isn't" choice and we honor it.
468 //
469 // We drain the payload bytes before visiting so the outer stream
470 // stays positioned after this enum value, then visit with
471 // `payload_already_consumed: true` so `unit_variant` doesn't try to
472 // re-read a `nil` that isn't there. The fallback is macro-validated
473 // to be a unit variant.
474 let fallback_tag =
475 if sum.reserved.contains(&tag) { sum.on_reserved_tag } else { sum.on_unknown_tag };
476 if let Some(fallback_tag) = fallback_tag {
477 let fallback = sum
478 .variants
479 .iter()
480 .find(|v| v.tag == fallback_tag)
481 .copied()
482 .expect("fallback tag must refer to a registered variant");
483 de::IgnoredAny::deserialize(&mut *self)?;
484 return visitor.visit_enum(TaggedEnumAccess {
485 parent: self,
486 variant: fallback,
487 payload_already_consumed: true,
488 });
489 }
490 Err(RmpError::custom(
491 format!("MsgpackTagged: unknown variant tag {tag} for enum {name:?}",),
492 ))
493 }
494
495 fn deserialize_identifier<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
496 (&mut self.inner).deserialize_identifier(visitor)
497 }
498
499 fn deserialize_ignored_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
500 (&mut self.inner).deserialize_ignored_any(visitor)
501 }
502
503 fn is_human_readable(&self) -> bool {
504 // msgpack is a binary format. rmp_serde lets users opt into a
505 // human-readable mode (`with_human_readable()`), but our wrapper
506 // currently only constructs the inner deserializer with the default
507 // config — and that default is binary.
508 false
509 }
510}
511
512impl<'a, 'de> Deserializer<'a, 'de> {
513 /// Resolve a registered `Product` by serde name. Used by
514 /// `deserialize_struct` (and, once it lands, `deserialize_tuple_struct`).
515 /// Mirrors `Serializer::product_for` — a registry miss or sum-shaped
516 /// entry is a real bug, so we panic loudly per the design doc rather
517 /// than fabricating a synthetic shape.
518 fn product_for(&self, name: &'static str) -> crate::Product {
519 let entry = self.registry.get(name).unwrap_or_else(|| {
520 panic!(
521 "MsgpackTagged registry miss for {name:?} — the top-level `register_into` \
522 walk should have registered every reachable type. Either the type is \
523 missing `#[derive(MsgpackTagged)]` (or a hand-written impl that calls \
524 `try_insert`), or its `serde` name doesn't match the registered name \
525 (check `#[serde(rename = ...)]`)"
526 )
527 });
528 entry.tagged().as_product().unwrap_or_else(|| {
529 panic!("registry entry for {name:?} is sum-shaped but a product shape was expected")
530 })
531 }
532
533 /// Resolve a registered `Sum` by enum-type name. Mirror of `product_for`
534 /// on the enum side, used by `deserialize_enum`.
535 fn sum_for(&self, name: &'static str) -> crate::Sum {
536 let entry = self.registry.get(name).unwrap_or_else(|| {
537 panic!(
538 "MsgpackTagged registry miss for enum {name:?} — the top-level \
539 `register_into` walk should have registered every reachable type"
540 )
541 });
542 entry.tagged().as_sum().unwrap_or_else(|| {
543 panic!("registry entry for {name:?} is product-shaped but a sum shape was expected")
544 })
545 }
546
547 /// Read `wire_len` `(u8 tag, value-bytes)` pairs from the inner reader
548 /// and return them as a `SmallVec` for later tag-driven dispatch. Used
549 /// by both `deserialize_tuple_struct` and `TaggedEnumAccess::tuple_variant`
550 /// — they share the int-keyed-map wire shape for tuple-shaped payloads
551 /// but their visitors call back positionally rather than by name, so we
552 /// can't dispatch entry-by-entry the way `TaggedProductMapAccess` does.
553 ///
554 /// Validates tag membership against the type's registered `Product`:
555 /// * Tag in `product.fields` (active position) — fine.
556 /// * Tag in `product.reserved` (retired position) — fine, the visitor
557 /// will simply never query it.
558 /// * Otherwise — only fine if `product.allow_unknown_tags` is set.
559 ///
560 /// Performs a cheap upfront cap check (`wire_len > active + reserved`)
561 /// when `allow_unknown_tags` is off, so grossly oversized wires error
562 /// before we walk the bytes. The per-tag scan after buffering catches
563 /// the within-bounds-but-still-unknown case.
564 ///
565 /// `context` is a caller-supplied closure producing a label (e.g.
566 /// `"tuple-struct \"Foo\""` or `"tuple variant \"Carry\""`) that gets
567 /// embedded in error messages. It's a closure so the `String` is only
568 /// allocated on the error path — the happy path skips the format call.
569 fn buffer_and_validate_int_keyed_map<'der>(
570 &'der mut self,
571 wire_len: usize,
572 product: &crate::Product,
573 context: impl FnOnce() -> String,
574 ) -> Result<IntKeyedEntries<'de>, RmpError> {
575 if !product.allow_unknown_tags {
576 let cap = product.fields.len() + product.reserved.len();
577 if wire_len > cap {
578 return Err(RmpError::custom(format!(
579 "{}: wire has {wire_len} entries but the type accepts \
580 at most {cap} ({} active + {} reserved); opt into \
581 `#[tagged(allow_unknown_tags)]` to silently skip extras",
582 context(),
583 product.fields.len(),
584 product.reserved.len(),
585 )));
586 }
587 }
588 let mut entries: IntKeyedEntries<'de> = SmallVec::with_capacity(wire_len);
589 for _ in 0..wire_len {
590 let tag: u8 = u8::deserialize(&mut *self)?;
591 let before: &'de [u8] = self.inner.get_mut();
592 de::IgnoredAny::deserialize(&mut *self)?;
593 let after: &'de [u8] = self.inner.get_mut();
594 let consumed = before.len() - after.len();
595 entries.push((tag, &before[..consumed]));
596 }
597 if !product.allow_unknown_tags {
598 for (tag, _) in &entries {
599 if product.field_for(*tag).is_none() && !product.is_reserved(*tag) {
600 return Err(RmpError::custom(format!(
601 "MsgpackTagged: unknown wire tag {tag} for {} \
602 and `allow_unknown_tags` is false",
603 context(),
604 )));
605 }
606 }
607 }
608 Ok(entries)
609 }
610
611 /// Read `wire_len` positional `value-bytes` entries from the inner
612 /// reader (the `Array`-strategy wire shape — `fixarray` of values, no
613 /// per-entry tag) and return them as an `IntKeyedEntries` with the tag
614 /// for each entry *synthesized* from the merged-sorted (active +
615 /// reserved) tag layout. This lets the downstream
616 /// `TaggedTupleStructAccess` dispatch by tag uniformly across both
617 /// wire strategies; for tuple structs / tuple variants the access
618 /// reuses the same tag-driven path.
619 ///
620 /// **Reserved-tag handling under Array.** When a V2 schema retires a
621 /// field with `#[tagged(reserved(N))]`, the V1 wire (produced before
622 /// the retirement) still carries a value at the slot that used to
623 /// hold tag `N`. The merged layout tells us which wire positions
624 /// correspond to retired tags so we can drain them silently without
625 /// confusing them with the active slots that come after.
626 ///
627 /// Cap check: `wire_len > active_count + reserved_count` is only
628 /// accepted under `allow_unknown_tags`. Entries beyond that arity
629 /// get consumed-and-discarded so the outer stream stays aligned, but
630 /// they're not pushed to the returned buffer.
631 fn buffer_positional_array<'der>(
632 &'der mut self,
633 wire_len: usize,
634 product: &crate::Product,
635 context: impl FnOnce() -> String,
636 ) -> Result<IntKeyedEntries<'de>, RmpError> {
637 let layout = merged_wire_layout(product);
638 if !product.allow_unknown_tags && wire_len > layout.len() {
639 return Err(RmpError::custom(format!(
640 "{}: wire has {wire_len} positional entries but the type accepts \
641 at most {} under Array strategy ({} active + {} reserved); opt \
642 into `#[tagged(allow_unknown_tags)]` to silently skip extras",
643 context(),
644 layout.len(),
645 product.fields.len(),
646 product.reserved.len(),
647 )));
648 }
649 let mut entries: IntKeyedEntries<'de> =
650 SmallVec::with_capacity(wire_len.min(product.fields.len()));
651 for wire_pos in 0..wire_len {
652 let before: &'de [u8] = self.inner.get_mut();
653 de::IgnoredAny::deserialize(&mut *self)?;
654 let after: &'de [u8] = self.inner.get_mut();
655 let consumed = before.len() - after.len();
656 match layout.get(wire_pos) {
657 // Active slot: push the value-bytes under the corresponding
658 // tag so the downstream tag-driven access can find it.
659 Some(&(tag, true)) => entries.push((tag, &before[..consumed])),
660 // Reserved slot: V2 doesn't have an active field here; the
661 // wire entry was V1's value at the retired position. Drain
662 // silently — the value bytes are already consumed by
663 // `IgnoredAny` above, and we don't push to the buffer.
664 Some(&(_, false)) => {}
665 // Trailing entry past `active + reserved`; cap check above
666 // only lets us reach here under `allow_unknown_tags`.
667 None => {}
668 }
669 }
670 Ok(entries)
671 }
672
673 /// Peek the next msgpack marker without advancing the reader and
674 /// classify it as a Tagged-strategy map header or an Array-strategy
675 /// array header. Used at every product-shaped decode site
676 /// (`deserialize_struct`, `deserialize_tuple_struct`,
677 /// `TaggedEnumAccess::tuple_variant` / `struct_variant`) to dispatch
678 /// to the right reader.
679 ///
680 /// Any other marker is a malformed-bytes error under
681 /// `Format::MsgpackTagged` — legacy `Msgpack` / `MsgpackCompact` data
682 /// has its own format byte and never reaches this wrapper, so
683 /// string-keyed maps and other shapes are not expected here.
684 fn peek_wire_shape(&mut self) -> Result<WireShape, RmpError> {
685 let bytes: &'de [u8] = self.inner.get_mut();
686 let first = *bytes.first().ok_or_else(|| {
687 RmpError::custom("expected fixmap or fixarray under MsgpackTagged, got end-of-stream")
688 })?;
689 match Marker::from_u8(first) {
690 Marker::FixMap(_) | Marker::Map16 | Marker::Map32 => Ok(WireShape::Map),
691 Marker::FixArray(_) | Marker::Array16 | Marker::Array32 => Ok(WireShape::Array),
692 m => Err(RmpError::custom(format!(
693 "expected fixmap or fixarray under MsgpackTagged, got {m:?}"
694 ))),
695 }
696 }
697}
698
699/// Wire shape of a product value under `Format::MsgpackTagged`. Picked per
700/// struct at decode time by peeking the next msgpack marker — the
701/// `Serializer` choice of `EncodingStrategy` is not communicated through a
702/// separate channel (and doesn't need to be: the shape is self-describing
703/// at the msgpack-header level).
704#[derive(Clone, Copy, Debug, PartialEq, Eq)]
705enum WireShape {
706 /// `fixmap` / `map16` / `map32` — Tagged: int-keyed map.
707 Map,
708 /// `fixarray` / `array16` / `array32` — Array: positional.
709 Array,
710}
711
712/// Merge `product.fields` and `product.reserved` into a single
713/// tag-ascending list of `(tag, is_active)` pairs.
714///
715/// The Array-strategy encoder emits wire positions in tag-ascending order
716/// across both active and reserved tags (a V1 writer that still carries a
717/// value at a now-retired tag sorts that tag in alongside the live ones).
718/// So decoding by wire position requires the same merged ordering: position
719/// `i` on the wire corresponds to the i-th tag in the merged list, and the
720/// `is_active` flag tells the decoder whether to push the value bytes into
721/// the buffered entries (active) or drain them silently (reserved).
722///
723/// Inline capacity 8 fits the typical ACIR product without spilling; larger
724/// products transparently fall back to the heap.
725fn merged_wire_layout(product: &crate::Product) -> SmallVec<[(u8, bool); 8]> {
726 let mut layout: SmallVec<[(u8, bool); 8]> =
727 SmallVec::with_capacity(product.fields.len() + product.reserved.len());
728 for &(tag, _) in product.fields {
729 layout.push((tag, true));
730 }
731 for &tag in product.reserved {
732 layout.push((tag, false));
733 }
734 layout.sort_by_key(|(tag, _)| *tag);
735 layout
736}
737
738/// Shared access adapter routing each yielded value through the parent
739/// [`Deserializer`]. The msgpack length-prefixed header (array length for
740/// sequences, map length for maps) is consumed up front in the
741/// corresponding `deserialize_*` method before this adapter is built; from
742/// there each element/key/value just reads its own bytes through the
743/// parent, so any nested tagged values still see this wrapper's
744/// interception.
745///
746/// Used as `SeqAccess` (e.g. `Vec<T>`, `&[T]`) and `MapAccess` (e.g.
747/// `BTreeMap<K, V>`). Once `deserialize_tuple` lands, fixed-length Rust
748/// tuples will share the `SeqAccess` impl too. Mirror of the serializer's
749/// `TaggedSerializeViaParent`.
750struct TaggedAccessViaParent<'der, 'a, 'de> {
751 parent: &'der mut Deserializer<'a, 'de>,
752 remaining: usize,
753}
754
755/// Variable-length sequences and fixed-length tuples — both wire-encoded
756/// as msgpack arrays. `next_element_seed` decrements `remaining` and
757/// deserializes one element through the parent.
758impl<'de, 'der, 'a> SeqAccess<'de> for TaggedAccessViaParent<'der, 'a, 'de> {
759 type Error = RmpError;
760
761 fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
762 where
763 T: DeserializeSeed<'de>,
764 {
765 if self.remaining == 0 {
766 return Ok(None);
767 }
768 self.remaining -= 1;
769 seed.deserialize(&mut *self.parent).map(Some)
770 }
771
772 fn size_hint(&self) -> Option<usize> {
773 Some(self.remaining)
774 }
775}
776
777/// Free-form maps. `next_key_seed` decrements `remaining` and deserializes
778/// the key; `next_value_seed` deserializes the value without
779/// decrementing (it pairs with the just-yielded key).
780impl<'de, 'der, 'a> MapAccess<'de> for TaggedAccessViaParent<'der, 'a, 'de> {
781 type Error = RmpError;
782
783 fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
784 where
785 K: DeserializeSeed<'de>,
786 {
787 if self.remaining == 0 {
788 return Ok(None);
789 }
790 self.remaining -= 1;
791 seed.deserialize(&mut *self.parent).map(Some)
792 }
793
794 fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
795 where
796 V: DeserializeSeed<'de>,
797 {
798 seed.deserialize(&mut *self.parent)
799 }
800
801 fn size_hint(&self) -> Option<usize> {
802 Some(self.remaining)
803 }
804}
805
806/// `MapAccess` adapter for tagged structs (and tuple structs once that
807/// lands). The wire is an int-keyed msgpack map — each entry's key is an
808/// integer tag that we translate back to the registered field name before
809/// handing it to the visitor (which expects a string identifier). Tags
810/// not in `product.fields` are either silently skipped (`allow_unknown_tags
811/// = true`) or rejected (the strict default).
812///
813/// Mirror of the serializer's `TaggedSerializeProduct` on the
814/// `SerializeStruct` case — same `(int_tag, value)` map shape, just
815/// driving the translation in the other direction.
816struct TaggedProductMapAccess<'der, 'a, 'de> {
817 parent: &'der mut Deserializer<'a, 'de>,
818 product: crate::Product,
819 /// Type name; only used for clearer error messages on unknown tags.
820 type_name: &'static str,
821 remaining: usize,
822}
823
824impl<'de, 'der, 'a> MapAccess<'de> for TaggedProductMapAccess<'der, 'a, 'de> {
825 type Error = RmpError;
826
827 fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
828 where
829 K: DeserializeSeed<'de>,
830 {
831 // Loop because unknown-but-skippable tags consume an entry from
832 // the wire without yielding one to the visitor — we then continue
833 // to the next entry. A `return Ok(None)` only happens when the
834 // map is exhausted, and a `return Ok(Some(...))` only happens
835 // when we have a known field name to hand to the visitor.
836 loop {
837 if self.remaining == 0 {
838 return Ok(None);
839 }
840 self.remaining -= 1;
841 let tag: u8 = u8::deserialize(&mut *self.parent)?;
842 if let Some(field_name) = self.product.field_for(tag) {
843 let key_deserializer =
844 de::value::BorrowedStrDeserializer::<RmpError>::new(field_name);
845 return seed.deserialize(key_deserializer).map(Some);
846 }
847 // Tag isn't an active field. Two ways to tolerate it:
848 // * `product.is_reserved(tag)` → the type explicitly retired
849 // this tag, so the user has opted into silent-skip for it
850 // (parallel to enums' `on_reserved` marker). Always silent.
851 // * `product.allow_unknown_tags` → the type opted into blanket
852 // forward-compat for *any* tag it doesn't recognize.
853 // Either branch consumes the value and loops to the next entry.
854 if self.product.is_reserved(tag) || self.product.allow_unknown_tags {
855 de::IgnoredAny::deserialize(&mut *self.parent)?;
856 continue;
857 }
858 return Err(RmpError::custom(format!(
859 "MsgpackTagged: unknown wire tag {tag} for product {:?} and \
860 `allow_unknown_tags` is false",
861 self.type_name,
862 )));
863 }
864 }
865
866 fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
867 where
868 V: DeserializeSeed<'de>,
869 {
870 seed.deserialize(&mut *self.parent)
871 }
872
873 fn size_hint(&self) -> Option<usize> {
874 Some(self.remaining)
875 }
876}
877
878/// `MapAccess` adapter for named structs decoded from the **Array**
879/// strategy wire shape (positional `fixarray` of values). Unlike
880/// `TaggedProductMapAccess` the wire carries no tags; we synthesize the
881/// keys yielded to the visitor by walking the merged (active + reserved)
882/// wire layout — see [`merged_wire_layout`] for why the merge is needed.
883/// The visitor still receives `(field_name, value)` pairs — that's how
884/// serde-derive's `Deserialize` for named structs is driven — but the
885/// field-name source is the registry, not the wire.
886///
887/// Reserved slots inside the layout are drained transparently: the wire
888/// position is consumed (so the outer stream stays aligned), but no
889/// `(field_name, value)` pair is yielded to the visitor.
890///
891/// Short wires (`wire_remaining < active count`) yield `Ok(None)` once
892/// exhausted; serde-derive's `#[serde(default)]` machinery fills in any
893/// missing trailing positions, same fallback as the Tagged path.
894///
895/// Long wires (`wire_remaining > layout.len()`) only reach this adapter
896/// when `allow_unknown_tags` is set — the cap check at the call site
897/// bails on the strict default. Extras past the layout get consumed-and-
898/// discarded inside `next_key_seed`'s loop before reporting exhaustion.
899struct ArrayProductMapAccess<'der, 'a, 'de> {
900 parent: &'der mut Deserializer<'a, 'de>,
901 product: crate::Product,
902 /// Merged (active + reserved) wire layout, tag-ascending. Drives the
903 /// per-position dispatch between "yield to visitor" (active) and
904 /// "drain silently" (reserved).
905 layout: SmallVec<[(u8, bool); 8]>,
906 /// Wire entries left to consume.
907 wire_remaining: usize,
908 /// Next position in the merged layout to consider.
909 next_position: usize,
910}
911
912impl<'de, 'der, 'a> MapAccess<'de> for ArrayProductMapAccess<'der, 'a, 'de> {
913 type Error = RmpError;
914
915 fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
916 where
917 K: DeserializeSeed<'de>,
918 {
919 loop {
920 // Wire exhausted: serde-derive's `#[serde(default)]` covers
921 // any trailing active positions the type still expects.
922 if self.wire_remaining == 0 {
923 return Ok(None);
924 }
925 match self.layout.get(self.next_position) {
926 // Reserved slot: discard the value bytes and advance both
927 // counters, then look at the next position.
928 Some(&(_, false)) => {
929 de::IgnoredAny::deserialize(&mut *self.parent)?;
930 self.wire_remaining -= 1;
931 self.next_position += 1;
932 }
933 // Active slot: synthesize the field-name key and hand it
934 // to the visitor. The matching `next_value_seed` call
935 // below advances the counters.
936 Some(&(tag, true)) => {
937 let field_name = self
938 .product
939 .field_for(tag)
940 .expect("active layout entry must resolve to a field name");
941 let key_deserializer =
942 de::value::BorrowedStrDeserializer::<RmpError>::new(field_name);
943 return seed.deserialize(key_deserializer).map(Some);
944 }
945 // Past the merged layout (only reachable under
946 // `allow_unknown_tags`). Drain remaining wire entries
947 // silently — never yield to the visitor.
948 None => {
949 de::IgnoredAny::deserialize(&mut *self.parent)?;
950 self.wire_remaining -= 1;
951 }
952 }
953 }
954 }
955
956 fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
957 where
958 V: DeserializeSeed<'de>,
959 {
960 let value = seed.deserialize(&mut *self.parent)?;
961 self.wire_remaining -= 1;
962 self.next_position += 1;
963 Ok(value)
964 }
965
966 fn size_hint(&self) -> Option<usize> {
967 // Upper bound: active positions still pending in the layout, capped
968 // by how many wire entries remain.
969 let active_remaining = self.layout[self.next_position.min(self.layout.len())..]
970 .iter()
971 .filter(|(_, active)| *active)
972 .count();
973 Some(active_remaining.min(self.wire_remaining))
974 }
975}
976
977/// `SeqAccess` adapter for top-level tuple structs (multi-element
978/// `struct Pair(u32, bool)` shapes). Decoded by tag, not by wire order:
979/// every wire entry is buffered as `(tag, value-bytes)` upfront, and on
980/// each `next_element_seed` we look up the tag corresponding to the
981/// current source position via `Product.tag_for("0")`, `tag_for("1")`,
982/// … and re-deserialize the matching entry's bytes through a freshly
983/// constructed sub-wrapper (so any nested tagged types still see this
984/// wrapper's interception).
985///
986/// This is what makes the deserializer robust to wire-order changes on
987/// the serializer side: if the serializer is fixed to emit tag-ascending
988/// (matching the design doc) — or some other order — we don't need to
989/// touch this code, because we route by tag.
990///
991/// **Buffering.** `value-bytes` is captured by snapshotting the inner
992/// reader's `&[u8]` before and after each `IgnoredAny::deserialize` walk
993/// (which advances exactly one msgpack value worth of bytes). Slicing
994/// the diff out of the original buffer gives us the exact byte range
995/// for that one value. The captured slice is `&'de [u8]` — same
996/// underlying buffer, no copy.
997///
998/// **Inline capacity.** `SmallVec<[_; 4]>` covers the typical tuple-
999/// struct arity (current ACIR/Brillig types use exclusively newtypes;
1000/// our test fixtures top out at 3-element tuple structs) without a
1001/// heap allocation. Anything larger transparently spills.
1002struct TaggedTupleStructAccess<'der, 'a, 'de> {
1003 parent: &'der mut Deserializer<'a, 'de>,
1004 product: crate::Product,
1005 /// Buffered `(tag, value-bytes)` pairs in wire arrival order. We look
1006 /// these up by tag in `next_element_seed`; insertion order doesn't
1007 /// matter.
1008 entries: IntKeyedEntries<'de>,
1009 next_position: usize,
1010}
1011
1012impl<'de, 'der, 'a> SeqAccess<'de> for TaggedTupleStructAccess<'der, 'a, 'de> {
1013 type Error = RmpError;
1014
1015 fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
1016 where
1017 T: DeserializeSeed<'de>,
1018 {
1019 let position = self.next_position;
1020 self.next_position += 1;
1021
1022 // Look up the wire tag for this source position. Wire-name strings
1023 // are positional (`"0"`, `"1"`, …) — the macro lifts these into
1024 // the registered `Product`'s `fields` slice. If the position is
1025 // beyond the type's arity, the product won't have it and we report
1026 // exhaustion to the visitor.
1027 let position_name = position.to_string();
1028 let Some(expected_tag) = self.product.tag_for(&position_name) else {
1029 return Ok(None);
1030 };
1031
1032 // Find the wire entry carrying that tag. If absent, report None —
1033 // serde-derive's visitor fills `#[serde(default)]` slots from
1034 // `Default` and reports `invalid length` on a missing required
1035 // position.
1036 let Some(&(_, value_bytes)) = self.entries.iter().find(|(tag, _)| *tag == expected_tag)
1037 else {
1038 return Ok(None);
1039 };
1040
1041 // Sub-wrapper over the value's bytes. Sharing the parent's
1042 // registry (via `Arc::clone`) keeps nested tagged-type lookups
1043 // consistent. The sub-deserializer's reader state starts fresh at
1044 // the value's first byte, so its own `marker` buffer is empty as
1045 // expected.
1046 let mut sub_deserializer = Deserializer::new(value_bytes, self.parent.registry);
1047 seed.deserialize(&mut sub_deserializer).map(Some)
1048 }
1049
1050 fn size_hint(&self) -> Option<usize> {
1051 Some(self.entries.len().saturating_sub(self.next_position))
1052 }
1053}
1054
1055/// `EnumAccess` + `VariantAccess` adapter for tagged enums.
1056///
1057/// `deserialize_enum` already consumed the outer `{u8 variant_tag: payload}`
1058/// map header and the variant tag, resolved the matching `Variant` from the
1059/// `Sum`, and handed both off to us. From here:
1060///
1061/// * `EnumAccess::variant_seed` yields the variant's serde *name* (as a
1062/// borrowed string) to the visitor — serde-derive's `__Field` visitor for
1063/// enums accepts identifiers as strings just like struct-field visitors do.
1064/// * The four `VariantAccess` methods dispatch on the kind the visitor
1065/// asks for (which is driven by the Rust declaration, not the wire), and
1066/// each reads the matching payload shape directly from `parent`:
1067/// - **unit** — consume the trailing `nil` written by the encode-side
1068/// `serialize_unit`.
1069/// - **newtype** — pass the bare inner value through `&mut *parent` so
1070/// any nested tagged types still recurse through the wrapper.
1071/// - **tuple** — read the int-keyed payload map and reuse the
1072/// `TaggedTupleStructAccess` buffering machinery; the variant's
1073/// `payload` `Product` carries the tag/position mapping.
1074/// - **struct** — read the int-keyed payload map and reuse
1075/// `TaggedProductMapAccess`; the variant's `payload` `Product` carries
1076/// the tag/field-name mapping.
1077struct TaggedEnumAccess<'der, 'a, 'de> {
1078 parent: &'der mut Deserializer<'a, 'de>,
1079 variant: crate::Variant,
1080 /// Set when `deserialize_enum` has already drained the wire payload —
1081 /// the only way to land here is the catch-all route, where the
1082 /// payload was discarded with `IgnoredAny` before `visit_enum`. The
1083 /// catch-all is always a unit variant per macro validation, so only
1084 /// `unit_variant` needs to consult this flag.
1085 payload_already_consumed: bool,
1086}
1087
1088impl<'de, 'der, 'a> EnumAccess<'de> for TaggedEnumAccess<'der, 'a, 'de> {
1089 type Error = RmpError;
1090 type Variant = Self;
1091
1092 fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
1093 where
1094 V: DeserializeSeed<'de>,
1095 {
1096 let key_deserializer =
1097 de::value::BorrowedStrDeserializer::<RmpError>::new(self.variant.name);
1098 let value = seed.deserialize(key_deserializer)?;
1099 Ok((value, self))
1100 }
1101}
1102
1103impl<'de, 'der, 'a> VariantAccess<'de> for TaggedEnumAccess<'der, 'a, 'de> {
1104 type Error = RmpError;
1105
1106 fn unit_variant(self) -> Result<(), Self::Error> {
1107 if self.payload_already_consumed {
1108 return Ok(());
1109 }
1110 <()>::deserialize(&mut *self.parent)
1111 }
1112
1113 fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Self::Error>
1114 where
1115 T: DeserializeSeed<'de>,
1116 {
1117 seed.deserialize(&mut *self.parent)
1118 }
1119
1120 fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
1121 where
1122 V: Visitor<'de>,
1123 {
1124 // Same dispatch as the top-level tuple-struct path: peek the
1125 // payload's wire shape, buffer into `IntKeyedEntries` either by
1126 // reading wire tags (Tagged) or by synthesizing them from the
1127 // payload's `Product.fields` (Array), then hand off to the
1128 // shared `TaggedTupleStructAccess`.
1129 let variant_name = self.variant.name;
1130 let context = || format!("tuple variant {variant_name:?}");
1131 let entries = match self.parent.peek_wire_shape()? {
1132 WireShape::Map => {
1133 let wire_len = self.parent.read_map_len()?;
1134 self.parent.buffer_and_validate_int_keyed_map(
1135 wire_len,
1136 &self.variant.payload,
1137 context,
1138 )?
1139 }
1140 WireShape::Array => {
1141 let wire_len = self.parent.read_array_len()?;
1142 self.parent.buffer_positional_array(wire_len, &self.variant.payload, context)?
1143 }
1144 };
1145 visitor.visit_seq(TaggedTupleStructAccess {
1146 parent: self.parent,
1147 product: self.variant.payload,
1148 entries,
1149 next_position: 0,
1150 })
1151 }
1152
1153 fn struct_variant<V>(
1154 self,
1155 _fields: &'static [&'static str],
1156 visitor: V,
1157 ) -> Result<V::Value, Self::Error>
1158 where
1159 V: Visitor<'de>,
1160 {
1161 // Mirror of `Deserializer::deserialize_struct`'s dispatch, on the
1162 // variant payload's `Product`.
1163 match self.parent.peek_wire_shape()? {
1164 WireShape::Map => {
1165 let wire_len = self.parent.read_map_len()?;
1166 visitor.visit_map(TaggedProductMapAccess {
1167 parent: self.parent,
1168 product: self.variant.payload,
1169 type_name: self.variant.name,
1170 remaining: wire_len,
1171 })
1172 }
1173 WireShape::Array => {
1174 let wire_len = self.parent.read_array_len()?;
1175 let layout = merged_wire_layout(&self.variant.payload);
1176 if !self.variant.payload.allow_unknown_tags && wire_len > layout.len() {
1177 return Err(RmpError::custom(format!(
1178 "struct variant {:?}: wire has {wire_len} positional entries but the \
1179 payload accepts at most {} under Array strategy ({} active + {} \
1180 reserved); opt into `#[tagged(allow_unknown_tags)]` to silently \
1181 skip extras",
1182 self.variant.name,
1183 layout.len(),
1184 self.variant.payload.fields.len(),
1185 self.variant.payload.reserved.len(),
1186 )));
1187 }
1188 visitor.visit_map(ArrayProductMapAccess {
1189 parent: self.parent,
1190 product: self.variant.payload,
1191 layout,
1192 wire_remaining: wire_len,
1193 next_position: 0,
1194 })
1195 }
1196 }
1197 }
1198}