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, &registry);
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}