Skip to main content

serde_saphyr/de/
deserializer.rs

1use std::collections::{HashSet, VecDeque};
2use std::{borrow::Cow, fmt};
3
4use granit_parser::ScalarStyle;
5use serde_core::de::{self, Deserializer as _, IntoDeserializer, Visitor};
6
7use super::base64::decode_base64_yaml;
8use super::cfg::Cfg;
9#[cfg(feature = "comments")]
10use super::commented_deser;
11use super::error::{Error, MissingFieldLocationGuard, TransformReason};
12use super::events::{
13    Ev, Events, RecordedEvents, ReplayEvents, attach_alias_locations_if_missing, eof_with_loc,
14    with_deferred_recursive_aliases,
15};
16use super::key_nodes::{
17    KeyFingerprint, KeyNode, PendingEntry, apply_duplicate_key_policy_to_entries,
18    capture_node_with_legacy_octal, capture_simple_tagged_node_as_map_events,
19    is_empty_mapping_key_fingerprint, is_merge_key, pending_entries_from_live_events,
20    simple_tagged_enum_name, validate_no_merge_keys_in_node_events,
21};
22#[cfg(feature = "properties")]
23use super::options::PropertySyntax;
24use super::options::{DuplicateKeyPolicy, MergeKeyPolicy, NonFiniteFloatPolicy};
25#[cfg(any(feature = "garde", feature = "validator"))]
26use super::path_map::PathRecorder;
27#[cfg(feature = "properties")]
28use super::properties::{interpolate_compose_style_with_limits, is_sole_unset_reference};
29use super::properties_redaction::{
30    ScalarRedactionCtx, ScalarRedactionGuard, with_interp_redaction_scope,
31};
32use super::spanned_deser;
33use super::tagged_deser;
34use super::tags::SfTag;
35use crate::anchor_store::{self, AnchorKind};
36#[cfg(feature = "properties")]
37use crate::budget::BudgetBreach;
38#[cfg(feature = "properties")]
39use crate::de_error::budget_error;
40use crate::location::Location;
41#[cfg(any(feature = "garde", feature = "validator"))]
42use crate::location::Locations;
43use crate::parse_scalars::{
44    leading_zero_decimal, maybe_not_string, parse_int_signed, parse_int_unsigned,
45    parse_yaml11_bool, parse_yaml12_bool, parse_yaml12_float, scalar_is_null, scalar_is_nullish,
46    try_parse_float_incl_overflow, yaml_trim,
47};
48
49struct TupleLenExpected {
50    len: usize,
51}
52
53impl fmt::Display for TupleLenExpected {
54    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
55        write!(formatter, "a tuple of size {}", self.len)
56    }
57}
58
59impl de::Expected for TupleLenExpected {
60    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
61        fmt::Display::fmt(self, formatter)
62    }
63}
64
65struct TupleLenVisitor<V> {
66    inner: V,
67    len: usize,
68}
69
70impl<'de, V> Visitor<'de> for TupleLenVisitor<V>
71where
72    V: Visitor<'de>,
73{
74    type Value = V::Value;
75
76    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
77        self.inner.expecting(formatter)
78    }
79
80    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
81    where
82        A: de::SeqAccess<'de>,
83    {
84        let mut consumed = 0usize;
85        let value = self.inner.visit_seq(CountingSeqAccess {
86            inner: &mut seq,
87            consumed: &mut consumed,
88        })?;
89
90        if seq.next_element::<de::IgnoredAny>()?.is_some() {
91            return Err(<A::Error as de::Error>::invalid_length(
92                consumed + 1,
93                &TupleLenExpected { len: self.len },
94            ));
95        }
96
97        Ok(value)
98    }
99}
100
101struct CountingSeqAccess<'a, A> {
102    inner: &'a mut A,
103    consumed: &'a mut usize,
104}
105
106impl<'de, A> de::SeqAccess<'de> for CountingSeqAccess<'_, A>
107where
108    A: de::SeqAccess<'de>,
109{
110    type Error = A::Error;
111
112    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
113    where
114        T: de::DeserializeSeed<'de>,
115    {
116        let value = self.inner.next_element_seed(seed)?;
117        if value.is_some() {
118            *self.consumed += 1;
119        }
120        Ok(value)
121    }
122
123    fn size_hint(&self) -> Option<usize> {
124        self.inner.size_hint()
125    }
126}
127
128fn skip_one_node_from_events(ev: &mut dyn Events<'_>) -> Result<(), Error> {
129    let mut depth;
130    match ev.next()? {
131        Some(Ev::RecursiveAlias { location, .. }) => {
132            return Err(Error::RecursiveReferencesRequireWeakTypes { location });
133        }
134        Some(Ev::Scalar { .. }) => return Ok(()),
135        Some(Ev::SeqStart { .. } | Ev::MapStart { .. }) => depth = 1usize,
136        Some(Ev::SeqEnd { location } | Ev::MapEnd { location }) => {
137            return Err(Error::UnexpectedContainerEndWhileSkippingNode { location });
138        }
139        Some(Ev::Taken { location }) => {
140            return Err(Error::unexpected("consumed event").with_location(location));
141        }
142        None => return Err(eof_with_loc(ev)),
143    }
144
145    while depth != 0 {
146        match ev.next()? {
147            Some(Ev::RecursiveAlias { location, .. }) => {
148                return Err(Error::RecursiveReferencesRequireWeakTypes { location });
149            }
150            Some(Ev::SeqStart { .. } | Ev::MapStart { .. }) => depth += 1,
151            Some(Ev::SeqEnd { .. } | Ev::MapEnd { .. }) => depth -= 1,
152            Some(Ev::Scalar { .. }) => {}
153            Some(Ev::Taken { location }) => {
154                return Err(Error::unexpected("consumed event").with_location(location));
155            }
156            None => return Err(eof_with_loc(ev)),
157        }
158    }
159
160    Ok(())
161}
162
163fn drain_remaining_sequence(ev: &mut dyn Events<'_>) -> Result<(), Error> {
164    loop {
165        match ev.peek()? {
166            Some(Ev::SeqEnd { .. }) => {
167                let _ = ev.next()?;
168                return Ok(());
169            }
170            Some(_) => skip_one_node_from_events(ev)?,
171            None => return Err(eof_with_loc(ev)),
172        }
173    }
174}
175
176/// Whether a mapping must remember the keys it has seen: duplicate-key policies and merge
177/// expansion read them back, but passing duplicates through without merging never does.
178fn remembers_seen_keys(cfg: &Cfg) -> bool {
179    !matches!(cfg.dup_policy, DuplicateKeyPolicy::PassThrough)
180        || cfg.merge_keys == MergeKeyPolicy::Merge
181}
182
183/// Whether `V` is Serde's private visitor for buffering an arbitrary value as
184/// `Content`, as used by `#[serde(untagged)]` and `#[serde(flatten)]`.
185///
186/// This intentionally does not match every visitor in Serde's private
187/// deserialization module. In particular, internally tagged enums use
188/// `TaggedContentVisitor`, whose input mapping must remain unchanged.
189fn is_serde_content_buffer<V>() -> bool {
190    let name = std::any::type_name::<V>();
191    // Recent rustc versions include the visitor's unused lifetime as `<'_>`;
192    // older versions omit it from `type_name`.
193    let bare_name = name
194        .split_once('<')
195        .map_or(name, |(bare_name, _)| bare_name);
196    name.contains("::private::de::") && bare_name.ends_with("::ContentVisitor")
197}
198
199/// The streaming Serde deserializer.
200///
201/// ## Important: this deserializer *borrows* and is only available in a closure
202///
203/// `YamlDeserializer` borrows from the YAML input processing state, so you generally
204/// cannot construct and return it as a standalone value.
205///
206/// Instead, obtain it through the `with_deserializer_from_*` helpers, which provide a
207/// [`crate::Deserializer`] (an alias for `YamlDeserializer`) **inside a closure**.
208///
209/// This is useful when you want to wrap the deserializer (for example, to collect
210/// ignored fields or to add error context) while still deserializing into your target type.
211///
212/// ## Example
213///
214/// ```rust
215/// use serde::Deserialize;
216///
217/// #[derive(Debug, Deserialize)]
218/// struct Config {
219///     host: String,
220///     port: u16,
221/// }
222///
223/// let yaml = "host: localhost\nport: 8080\n";
224///
225/// let cfg: Config = serde_saphyr::with_deserializer_from_str(yaml,
226///     |de: serde_saphyr::Deserializer| {
227///         Config::deserialize(de)
228/// })?;
229///
230/// assert_eq!(cfg.port, 8080);
231/// # Ok::<(), serde_saphyr::Error>(())
232/// ```
233///
234/// This type is *stateless* with respect to ownership: it borrows the underlying input
235/// state (`'e`) and forwards Serde requests into it, translating YAML shapes into Serde calls.
236// Where do values come from: From an `Events` stream (typically [`LiveEvents`])
237// that yields simplified YAML events.
238// Where do values go: Into a Serde `Visitor` provided by the caller's
239// `T: Deserialize`, which drives how we walk the event stream and construct `T`.
240pub struct YamlDeserializer<'de, 'e> {
241    pub(super) ev: &'e mut dyn Events<'de>,
242    pub(super) cfg: Cfg,
243    /// True when deserializing a map key.
244    in_key: bool,
245    /// True when Serde entered through `deserialize_struct`.
246    ///
247    /// Derived struct visitors reject repeated fields themselves, so `LastWins`
248    /// needs duplicate fields collapsed before they reach Serde.
249    struct_mode: bool,
250    /// True when the recorded key node was exactly an empty mapping (`MapStart` followed by `MapEnd`).
251    key_empty_map_node: bool,
252    /// Comments that the parent mapping associated with this value.
253    pub(super) pending_comments: Vec<Cow<'de, str>>,
254    /// Same-line comments after the parent container separator (`key:` or `-`).
255    pub(super) pending_value_separator_comments: Vec<Cow<'de, str>>,
256    /// Comments that appeared above the value node itself.
257    pub(super) pending_value_comments: Vec<Cow<'de, str>>,
258
259    #[cfg(any(feature = "garde", feature = "validator"))]
260    pub(super) garde: Option<&'e mut PathRecorder>,
261}
262
263#[derive(Clone)]
264struct ScalarView<'de> {
265    raw: Cow<'de, str>,
266    effective: Cow<'de, str>,
267    tag: SfTag,
268    style: ScalarStyle,
269    location: Location,
270    interpolated: bool,
271}
272
273type EffectiveScalar<'de> = (Cow<'de, str>, SfTag, ScalarStyle, Location);
274
275impl ScalarView<'_> {
276    fn redaction_ctx(&self) -> Option<ScalarRedactionCtx> {
277        self.interpolated.then(|| ScalarRedactionCtx {
278            raw: self.raw.as_ref().to_owned(),
279            effective: self.effective.as_ref().to_owned(),
280        })
281    }
282}
283
284fn with_scalar_redaction<T>(
285    ctx: Option<ScalarRedactionCtx>,
286    f: impl FnOnce() -> Result<T, Error>,
287) -> Result<T, Error> {
288    let _guard = ctx.map(ScalarRedactionGuard::new);
289    f()
290}
291
292/// Runs one nested deserialize boundary inside its own subtree redaction scope while also
293/// seeding that scope with the immediate scalar context when available.
294///
295/// Called from sequence/map seed boundaries and enum newtype payload accessors so any error
296/// raised after child deserialization can still redact interpolated values seen within that
297/// subtree.
298fn with_subtree_redaction<T>(
299    ctx: Option<ScalarRedactionCtx>,
300    f: impl FnOnce() -> Result<T, Error>,
301) -> Result<T, Error> {
302    with_interp_redaction_scope(|| with_scalar_redaction(ctx, f))
303}
304
305#[cfg(feature = "properties")]
306pub(crate) fn with_root_redaction<'de, 'e, T>(
307    mut de: YamlDeserializer<'de, 'e>,
308    f: impl FnOnce(YamlDeserializer<'de, 'e>) -> Result<T, Error>,
309) -> Result<T, Error> {
310    let redaction_ctx = de.peek_scalar_redaction_ctx()?;
311    with_subtree_redaction(redaction_ctx, || f(de))
312}
313
314#[cfg(not(feature = "properties"))]
315pub(crate) fn with_root_redaction<'de, 'e, T>(
316    de: YamlDeserializer<'de, 'e>,
317    f: impl FnOnce(YamlDeserializer<'de, 'e>) -> Result<T, Error>,
318) -> Result<T, Error> {
319    f(de)
320}
321
322/// Deserialize a document unless its root denotes a null document.
323///
324/// Share the root scalar view between null detection and redaction so probing a document
325/// does not add an interpolation pass. Application tags must reach the target's dispatch.
326#[cfg_attr(not(feature = "properties"), allow(unused_mut))]
327pub(crate) fn with_root_redaction_unless_null<'de, 'e, T>(
328    mut de: YamlDeserializer<'de, 'e>,
329    f: impl FnOnce(YamlDeserializer<'de, 'e>) -> Result<T, Error>,
330) -> Result<Option<T>, Error> {
331    if let Some(Ev::Scalar {
332        value, tag, style, ..
333    }) = de.ev.peek()?
334    {
335        let keep_tagged = *tag == SfTag::Other;
336        if !keep_tagged && scalar_is_null(tag, value, style) {
337            let _ = de.ev.next()?;
338            return Ok(None);
339        }
340
341        #[cfg(feature = "properties")]
342        {
343            let (tag, style) = (*tag, *style);
344            if de.interpolation_possible(tag, style) {
345                let Some(view) = de.peek_scalar_view()? else {
346                    return Err(eof_with_loc(de.ev));
347                };
348                if !keep_tagged
349                    && de.ev.property_interpolation().syntax()
350                        == PropertySyntax::DockerComposeWithNulls
351                    && view.tag == SfTag::Null
352                {
353                    let _ = de.ev.next()?;
354                    return Ok(None);
355                }
356                return with_subtree_redaction(view.redaction_ctx(), || f(de)).map(Some);
357            }
358        }
359    }
360
361    with_root_redaction(de, f).map(Some)
362}
363
364struct EnumScalarId<'de> {
365    raw: Cow<'de, str>,
366    effective: Cow<'de, str>,
367    interpolated: bool,
368    location: Location,
369}
370
371impl<'de> EnumScalarId<'de> {
372    fn from_view(view: ScalarView<'de>) -> Self {
373        Self {
374            raw: view.raw,
375            effective: view.effective,
376            interpolated: view.interpolated,
377            location: view.location,
378        }
379    }
380
381    fn redaction_ctx(&self) -> Option<ScalarRedactionCtx> {
382        self.interpolated.then(|| ScalarRedactionCtx {
383            raw: self.raw.as_ref().to_owned(),
384            effective: self.effective.as_ref().to_owned(),
385        })
386    }
387}
388
389impl<'de> IntoDeserializer<'de, Error> for EnumScalarId<'de> {
390    type Deserializer = Self;
391
392    fn into_deserializer(self) -> Self::Deserializer {
393        self
394    }
395}
396
397impl<'de> de::Deserializer<'de> for EnumScalarId<'de> {
398    type Error = Error;
399
400    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
401    where
402        V: Visitor<'de>,
403    {
404        self.deserialize_identifier(visitor)
405    }
406
407    fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error>
408    where
409        V: Visitor<'de>,
410    {
411        let ctx = self.redaction_ctx();
412        let location = self.location;
413        let effective = self.effective;
414        with_scalar_redaction(ctx, move || match effective {
415            Cow::Borrowed(value) => visitor.visit_borrowed_str(value),
416            Cow::Owned(value) => visitor.visit_string(value),
417        })
418        .map_err(|err| err.with_location(location))
419    }
420
421    serde_core::forward_to_deserialize_any! {
422        bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf
423        option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum
424        ignored_any
425    }
426}
427
428impl<'de, 'e> YamlDeserializer<'de, 'e> {
429    /// Check whether an explicit tag is compatible with a scalar target.
430    ///
431    /// `tag` is the node's resolved tag, `expected` is the target's scalar tag,
432    /// `ty` names the target in diagnostics, and `location` identifies the node.
433    /// Returns an error for incompatible tags or disabled robotics conversions.
434    fn validate_scalar_tag(
435        &self,
436        tag: SfTag,
437        expected: SfTag,
438        ty: &'static str,
439        location: Location,
440    ) -> Result<(), Error> {
441        let compatible = tag == expected
442            || match tag {
443                SfTag::None
444                | SfTag::Other
445                | SfTag::Include
446                | SfTag::Merge
447                | SfTag::Value
448                | SfTag::NonSpecific => true,
449                tag if tag.is_robotics() => {
450                    expected == SfTag::Float
451                        && cfg!(feature = "robotics")
452                        && self.cfg.angle_conversions
453                }
454                _ => false,
455            };
456        if compatible {
457            Ok(())
458        } else {
459            Err(Error::InvalidScalar { ty, location })
460        }
461    }
462
463    /// Construct a new streaming deserializer over an `Events` source.
464    ///
465    /// Arguments:
466    /// - `ev`: the event source (e.g., `LiveEvents` or `ReplayEvents`).
467    /// - `cfg`: small by-copy configuration affecting parsing policies.
468    ///
469    /// Returns:
470    /// - `Deser` ready to be handed to Serde.
471    ///
472    /// Called by:
473    /// - Top-level entry points and recursively for nested values.
474    pub(crate) fn new(ev: &'e mut dyn Events<'de>, cfg: Cfg) -> Self {
475        Self {
476            ev,
477            cfg,
478            in_key: false,
479            struct_mode: false,
480            key_empty_map_node: false,
481            pending_comments: Vec::new(),
482            pending_value_separator_comments: Vec::new(),
483            pending_value_comments: Vec::new(),
484
485            #[cfg(any(feature = "garde", feature = "validator"))]
486            garde: None,
487        }
488    }
489
490    fn quoting_required_for_scalar(view: &ScalarView<'de>) -> Error {
491        Error::quoting_required(view.raw.as_ref(), view.interpolated).with_location(view.location)
492    }
493
494    #[cfg(feature = "properties")]
495    fn interpolation_possible(&self, tag: SfTag, style: ScalarStyle) -> bool {
496        if self.in_key || tag == SfTag::Binary {
497            return false;
498        }
499
500        let properties = self.ev.property_interpolation();
501        if properties.property_map().is_none() {
502            return false;
503        }
504
505        match properties.syntax() {
506            syntax if syntax.is_docker_compose() => tag.can_parse_into_string(),
507            _ => style == ScalarStyle::Plain,
508        }
509    }
510
511    #[cfg(not(feature = "properties"))]
512    fn interpolation_possible(&self, _tag: SfTag, _style: ScalarStyle) -> bool {
513        false
514    }
515
516    #[cfg(feature = "properties")]
517    fn peek_scalar_redaction_ctx(&mut self) -> Result<Option<ScalarRedactionCtx>, Error> {
518        let Some((tag, style)) = (match self.ev.peek()? {
519            Some(Ev::Scalar { tag, style, .. }) => Some((*tag, *style)),
520            _ => None,
521        }) else {
522            return Ok(None);
523        };
524
525        if !self.interpolation_possible(tag, style) {
526            return Ok(None);
527        }
528
529        Ok(self
530            .peek_scalar_view()?
531            .and_then(|view| view.redaction_ctx()))
532    }
533
534    #[cfg(not(feature = "properties"))]
535    fn peek_scalar_redaction_ctx(&mut self) -> Result<Option<ScalarRedactionCtx>, Error> {
536        Ok(None)
537    }
538
539    #[cfg(any(feature = "garde", feature = "validator"))]
540    pub(crate) fn new_with_path_recorder(
541        ev: &'e mut dyn Events<'de>,
542        cfg: Cfg,
543        garde: &'e mut PathRecorder,
544    ) -> Self {
545        Self {
546            ev,
547            cfg,
548            in_key: false,
549            struct_mode: false,
550            key_empty_map_node: false,
551            pending_comments: Vec::new(),
552            pending_value_separator_comments: Vec::new(),
553            pending_value_comments: Vec::new(),
554            garde: Some(garde),
555        }
556    }
557
558    /// Consume the next scalar event and return `(value, tag, location)`.
559    ///
560    /// Returns:
561    /// - `Ok((String, Option<String>, Location))` on scalar,
562    /// - `Err(Error)` otherwise.
563    ///
564    /// Called by:
565    /// - Numeric/bool/char parsers and `take_string_scalar`.
566    fn take_scalar_event(&mut self) -> Result<(String, SfTag, Location), Error> {
567        let view = self.take_scalar_view()?;
568        Ok((view.effective.into_owned(), view.tag, view.location))
569    }
570
571    /// Consume the next scalar event and return it without allocating a new `String` (if possible).
572    ///
573    /// This keeps the scalar text in its existing `Cow` container, which is cheap to clone
574    /// and allows primitive parsers (bool/int/float/char) to work directly on `&str`.
575    fn take_scalar_cow_event(&mut self) -> Result<(Cow<'de, str>, SfTag, Location), Error> {
576        let view = self.take_scalar_view()?;
577        Ok((view.effective, view.tag, view.location))
578    }
579
580    fn take_scalar_view(&mut self) -> Result<ScalarView<'de>, Error> {
581        match self.ev.next()? {
582            Some(Ev::Scalar {
583                value,
584                tag,
585                style,
586                location,
587                ..
588            }) => self.scalar_view_from_parts(value, tag, style, location),
589            Some(other) => Err(Error::unexpected("string scalar").with_location(other.location())),
590            None => Err(eof_with_loc(self.ev)),
591        }
592    }
593
594    fn take_peeked_scalar_view(&mut self, view: ScalarView<'de>) -> Result<ScalarView<'de>, Error> {
595        match self.ev.next()? {
596            Some(Ev::Scalar { .. }) => Ok(view),
597            Some(other) => Err(Error::unexpected("string scalar").with_location(other.location())),
598            None => Err(eof_with_loc(self.ev)),
599        }
600    }
601
602    /// Read a scalar as `String`, decoding `!!binary` into UTF-8 text if needed.
603    ///
604    /// Errors if the tag is incompatible with strings or if the binary payload
605    /// is not valid UTF-8.
606    fn take_string_scalar(&mut self) -> Result<String, Error> {
607        let (value, tag, location) = self.take_scalar_event()?;
608
609        // Special-case binary: decode base64 and require valid UTF-8.
610        if tag == SfTag::Binary && !self.cfg.ignore_binary_tag_for_string {
611            let data = decode_base64_yaml(&value).map_err(|err| err.with_location(location))?;
612            let text = String::from_utf8(data).map_err(|_| Error::BinaryNotUtf8 { location })?;
613            return Ok(text);
614        }
615
616        // For non-binary, ensure the tag allows string deserialization.
617        if !(tag.can_parse_into_string()
618            || self.cfg.ignore_binary_tag_for_string && tag == SfTag::Binary)
619        {
620            return Err(Error::TaggedScalarCannotDeserializeIntoString { location });
621        }
622
623        Ok(value)
624    }
625
626    /// Deliver a float to a typeless visitor, applying the configured non-finite policy.
627    fn visit_typeless_float<V: Visitor<'de>>(
628        &self,
629        value: f64,
630        raw: String,
631        location: Location,
632        visitor: V,
633    ) -> Result<V::Value, Error> {
634        if value.is_finite() {
635            return visitor.visit_f64(value);
636        }
637        match self.cfg.non_finite_float_policy {
638            NonFiniteFloatPolicy::PassThrough => visitor.visit_f64(value),
639            // Cfg resolves the compatibility default; an unconfigured policy otherwise
640            // has the same behavior as Reject.
641            NonFiniteFloatPolicy::Default | NonFiniteFloatPolicy::Reject => {
642                Err(Error::NonFiniteFloat {
643                    value: raw,
644                    location,
645                })
646            }
647            NonFiniteFloatPolicy::AsString => {
648                let canonical = if value.is_nan() {
649                    ".nan"
650                } else if value.is_sign_negative() {
651                    "-.inf"
652                } else {
653                    ".inf"
654                };
655                visitor.visit_string(canonical.to_owned())
656            }
657        }
658    }
659
660    /// Expect a sequence start and consume it, or error otherwise.
661    fn expect_seq_start(&mut self) -> Result<(), Error> {
662        match self.ev.next()? {
663            Some(Ev::SeqStart { .. }) => Ok(()),
664            Some(other) => Err(Error::unexpected("sequence start").with_location(other.location())),
665            None => Err(eof_with_loc(self.ev)),
666        }
667    }
668
669    /// Expect a mapping start and consume it, or error otherwise.
670    fn expect_map_start(&mut self) -> Result<(), Error> {
671        match self.ev.next()? {
672            Some(Ev::MapStart { .. }) => Ok(()),
673            Some(other) => Err(Error::unexpected("mapping start").with_location(other.location())),
674            None => Err(eof_with_loc(self.ev)),
675        }
676    }
677
678    fn peek_effective_scalar(&mut self) -> Result<Option<EffectiveScalar<'de>>, Error> {
679        let Some(view) = self.peek_scalar_view()? else {
680            return Ok(None);
681        };
682        Ok(Some((view.effective, view.tag, view.style, view.location)))
683    }
684
685    /// Check for a null scalar without consuming it.
686    ///
687    /// Structural consumers historically inspect the raw event. Only the opt-in Compose
688    /// null mode also resolves an eligible interpolation here; other modes keep that behavior.
689    pub(super) fn peek_null_value(&mut self) -> Result<bool, Error> {
690        match self.ev.peek()? {
691            Some(Ev::Scalar {
692                value, tag, style, ..
693            }) => {
694                if scalar_is_null(tag, value, style) {
695                    return Ok(true);
696                }
697
698                #[cfg(feature = "properties")]
699                {
700                    let (tag, style) = (*tag, *style);
701                    if self.ev.property_interpolation().syntax()
702                        == PropertySyntax::DockerComposeWithNulls
703                        && !tag.forces_string()
704                        && self.interpolation_possible(tag, style)
705                    {
706                        return Ok(self
707                            .peek_scalar_view()?
708                            .is_some_and(|view| view.tag == SfTag::Null));
709                    }
710                }
711
712                Ok(false)
713            }
714            _ => Ok(false),
715        }
716    }
717
718    fn peek_scalar_view(&mut self) -> Result<Option<ScalarView<'de>>, Error> {
719        let (value, tag, style, location) = match self.ev.peek()? {
720            Some(Ev::Scalar {
721                value,
722                tag,
723                style,
724                location,
725                ..
726            }) => (value.clone(), *tag, *style, *location),
727            _ => return Ok(None),
728        };
729
730        Ok(Some(
731            self.scalar_view_from_parts(value, tag, style, location)?,
732        ))
733    }
734
735    fn scalar_view_from_parts(
736        &self,
737        raw: Cow<'de, str>,
738        tag: SfTag,
739        style: ScalarStyle,
740        location: Location,
741    ) -> Result<ScalarView<'de>, Error> {
742        let can_interpolate = self.interpolation_possible(tag, style);
743        let effective = if can_interpolate {
744            self.effective_scalar_value(raw.clone(), location)?
745        } else {
746            raw.clone()
747        };
748        let interpolated = can_interpolate && raw.as_ref() != effective.as_ref();
749        #[cfg(feature = "properties")]
750        let tag = if interpolated {
751            self.compose_expansion_tag(&raw, &effective, tag)
752        } else {
753            tag
754        };
755        Ok(ScalarView {
756            raw,
757            effective,
758            tag,
759            style,
760            location,
761            interpolated,
762        })
763    }
764
765    /// How an interpolated scalar is resolved under the Compose syntaxes.
766    ///
767    /// Compose parses YAML before interpolation. An expanded string stays a string even
768    /// when its contents look like null, a boolean, or a number. The non-specific tag
769    /// preserves this for generic visitors while still allowing explicitly requested
770    /// numeric types to parse the result.
771    ///
772    /// [`PropertySyntax::DockerComposeWithNulls`] instead tags a value that should be null
773    /// (see its docs) as `!!null`, so every target treats it exactly like a literal null.
774    /// Explicit and string-forcing tags are left alone by both modes.
775    #[cfg(feature = "properties")]
776    fn compose_expansion_tag(&self, raw: &str, effective: &str, tag: SfTag) -> SfTag {
777        let properties = self.ev.property_interpolation();
778        let syntax = properties.syntax();
779        if !syntax.is_docker_compose() || !tag.can_parse_into_string() || tag.forces_string() {
780            return tag;
781        }
782        let becomes_null = syntax == PropertySyntax::DockerComposeWithNulls
783            && (matches!(effective, "~" | "null" | "Null" | "NULL")
784                || properties
785                    .property_map()
786                    .is_some_and(|vars| is_sole_unset_reference(raw, vars, syntax)));
787        if becomes_null {
788            SfTag::Null
789        } else {
790            SfTag::NonSpecific
791        }
792    }
793
794    /// Expand properties after the caller has checked scalar interpolation eligibility.
795    #[cfg(feature = "properties")]
796    fn effective_scalar_value(
797        &self,
798        value: Cow<'de, str>,
799        location: Location,
800    ) -> Result<Cow<'de, str>, Error> {
801        let properties = self.ev.property_interpolation();
802        let Some(vars) = properties.property_map() else {
803            return Ok(value);
804        };
805        let vars = vars.as_ref();
806        let syntax = properties.syntax();
807
808        match interpolate_compose_style_with_limits(
809            value,
810            vars,
811            syntax,
812            properties.max_expansion_depth(),
813            properties.max_total_work(),
814            properties.total_work(),
815        ) {
816            Ok(value) => Ok(value),
817            Err(crate::properties::PropertyError::Unresolved(name)) => {
818                Err(Error::UnresolvedProperty { name, location })
819            }
820            Err(crate::properties::PropertyError::InvalidName(name)) => {
821                Err(Error::InvalidPropertyName { name, location })
822            }
823            Err(crate::properties::PropertyError::RequiredButUnset { name, message }) => {
824                Err(Error::PropertyRequiredButUnset {
825                    name,
826                    message,
827                    location,
828                })
829            }
830            Err(crate::properties::PropertyError::RequiredButEmpty { name, message }) => {
831                Err(Error::PropertyRequiredButEmpty {
832                    name,
833                    message,
834                    location,
835                })
836            }
837            Err(crate::properties::PropertyError::ExpansionDepthLimitExceeded {
838                depth,
839                max_depth,
840            }) => {
841                let breach = BudgetBreach::PropertyExpansionDepth { depth, max_depth };
842                properties.record_breach(breach.clone());
843                Err(budget_error(breach).with_location(location))
844            }
845            Err(crate::properties::PropertyError::ExpansionWorkLimitExceeded {
846                work,
847                max_work,
848            }) => {
849                let breach = BudgetBreach::PropertyInterpolationWork { work, max_work };
850                properties.record_breach(breach.clone());
851                Err(budget_error(breach).with_location(location))
852            }
853        }
854    }
855
856    #[cfg(not(feature = "properties"))]
857    fn effective_scalar_value(
858        &self,
859        value: Cow<'de, str>,
860        _location: Location,
861    ) -> Result<Cow<'de, str>, Error> {
862        Ok(value)
863    }
864
865    /// Peek at the next event's anchor id, if any (0 indicates no anchor).
866    fn peek_anchor_id(&mut self) -> Result<Option<usize>, Error> {
867        match self.ev.peek()? {
868            Some(
869                Ev::Scalar { anchor, .. }
870                | Ev::SeqStart { anchor, .. }
871                | Ev::MapStart { anchor, .. },
872            ) => {
873                if *anchor == 0 {
874                    Ok(None)
875                } else {
876                    Ok(Some(*anchor))
877                }
878            }
879            _ => Ok(None),
880        }
881    }
882}
883
884impl<'de> de::Deserializer<'de> for YamlDeserializer<'de, '_> {
885    type Error = Error;
886
887    /// Fallback entry point when the caller's type has no specific expectation.
888    ///
889    /// When does Serde call this?
890    /// - When the caller (Serde) does not know the exact Rust type to deserialize yet and
891    ///   wants the format to "do the best it can" from the data. This happens, for example,
892    ///   inside some enum deserialization strategies, in erased/typeless positions (e.g. Value-like
893    ///   seeds), or when visitor-based APIs defer the concrete type decision.
894    /// - Even for structs/enums, Serde may call `deserialize_any` for individual field values
895    ///   when the driving logic cannot or does not specify a concrete numeric/bool/char method.
896    ///
897    /// Can we force Serde to call the typed methods (`deserialize_u8`, `deserialize_bool`, ...)?
898    /// - Not from within a format Deserializer. Serde chooses which method to call based on the
899    ///   Rust type information it has via the caller’s `Deserialize`/`DeserializeSeed` logic.
900    ///   Implementing the typed methods (which we do) ensures Serde will use them whenever it knows
901    ///   the target type; otherwise, it falls back to `deserialize_any`.
902    ///
903    /// Can we learn the target field’s Rust type from here?
904    /// - No. Serde does not expose type reflection to Deserializers. The only hint we get is which
905    ///   method Serde chose to call. Field names are available in `deserialize_struct`, but not the
906    ///   field types.
907    ///
908    /// Our policy:
909    /// - Explicit core scalar tags and enabled robotics tags select the corresponding type
910    ///   regardless of scalar style.
911    ///   Plain, untagged scalars are inferred (null-like → bool → int → float) before falling
912    ///   back to string. Quoted scalars without a core type tag remain strings, and !!binary
913    ///   scalars are decoded as strings according to the configured binary-tag policy.
914    ///
915    /// Flow: We inspect the next event; scalars are parsed with the heuristic above; containers
916    /// delegate to `deserialize_seq`/`deserialize_map`.
917    fn deserialize_any<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
918        // Only Serde's generic Content buffer needs tagged nodes represented as
919        // synthetic maps. General untyped visitors discard YAML tags, while other
920        // private Serde visitors may attach their own meaning to the original map.
921        if is_serde_content_buffer::<V>()
922            && let Some(events) = capture_simple_tagged_node_as_map_events(self.ev)?
923        {
924            let mut replay = ReplayEvents::new(
925                events,
926                #[cfg(feature = "properties")]
927                self.ev.property_interpolation().clone(),
928            );
929            return YamlDeserializer::new(&mut replay, self.cfg).deserialize_map(visitor);
930        }
931
932        if let Some((value, tag, style, location)) = self.peek_effective_scalar()? {
933            // Explicit scalar types take precedence over implicit null and string inference,
934            // including for quoted and block scalars buffered by Serde's flatten support.
935            match tag {
936                SfTag::Null => {
937                    let _ = self.take_scalar_event()?;
938                    return visitor.visit_unit();
939                }
940                SfTag::Bool => return self.deserialize_bool(visitor),
941                SfTag::Int if yaml_trim(&value).starts_with('-') => {
942                    return self.deserialize_i64(visitor);
943                }
944                SfTag::Int => return self.deserialize_u64(visitor),
945                SfTag::Float | SfTag::Time | SfTag::Degrees | SfTag::Radians => {
946                    self.validate_scalar_tag(tag, SfTag::Float, "floating point", location)?;
947                    let view = self.take_scalar_view()?;
948                    let value = try_parse_float_incl_overflow(
949                        &view.effective,
950                        view.location,
951                        view.tag,
952                        self.cfg.angle_conversions,
953                    )
954                    .ok_or(Error::InvalidScalar {
955                        ty: "floating point",
956                        location: view.location,
957                    })?;
958                    return self.visit_typeless_float(
959                        value,
960                        view.raw.into_owned(),
961                        view.location,
962                        visitor,
963                    );
964                }
965                _ => {}
966            }
967            let is_plain = matches!(style, ScalarStyle::Plain);
968            // String tags (!!str and !) preserve null-like text (null, ~, empty).
969            if scalar_is_null(&tag, &value, &style) {
970                let _ = self.ev.next()?; // consume
971                return visitor.visit_unit();
972            }
973            if !is_plain
974                || !tag.can_parse_into_string()
975                || tag == SfTag::Binary
976                || tag.forces_string()
977            {
978                // For string-ish scalars, rely on the parser's own zero-copy capability:
979                // if the scalar is returned as `Cow::Borrowed`, we can pass it through.
980                // Otherwise we fall back to owning.
981                if tag == SfTag::Binary && !self.cfg.ignore_binary_tag_for_string {
982                    return visitor.visit_string(self.take_string_scalar()?);
983                }
984                if !(tag.can_parse_into_string()
985                    || self.cfg.ignore_binary_tag_for_string && tag == SfTag::Binary)
986                {
987                    return Err(Error::TaggedScalarCannotDeserializeIntoString { location });
988                }
989
990                let (cow, _tag2, _location) = self.take_scalar_cow_event()?;
991                return match cow {
992                    Cow::Borrowed(b) => visitor.visit_borrowed_str(b),
993                    Cow::Owned(s) => visitor.visit_string(s),
994                };
995            }
996
997            // Consume the scalar and attempt typed parses in order: bool -> int -> float.
998            // Parse the effective value, but keep the raw source text for diagnostics.
999            let view = self.take_scalar_view()?;
1000            let effective = view.effective;
1001            let location = view.location;
1002
1003            // Try booleans.
1004            if self.cfg.strict_booleans {
1005                if let Some(b) = parse_yaml12_bool(&effective) {
1006                    return visitor.visit_bool(b);
1007                }
1008            } else if let Some(b) = parse_yaml11_bool(&effective) {
1009                return visitor.visit_bool(b);
1010            }
1011
1012            let t = yaml_trim(&effective);
1013            // Preserve selected spellings before either numeric parser can consume
1014            // them. Explicit numeric tags have already dispatched above.
1015            if (self.cfg.leading_zero_numbers_as_strings && leading_zero_decimal(t))
1016                || (self.cfg.underscored_numbers_as_strings && t.contains('_'))
1017            {
1018                return match effective {
1019                    Cow::Borrowed(b) => visitor.visit_borrowed_str(b),
1020                    Cow::Owned(s) => visitor.visit_string(s),
1021                };
1022            }
1023
1024            // Try integers: signed if leading '-', else unsigned, using 64-bit visitors.
1025            if t.starts_with('-') {
1026                if (!leading_zero_decimal(t) || self.cfg.legacy_octal_numbers)
1027                    && let Ok(v) =
1028                        parse_int_signed::<i64>(t, "i64", location, self.cfg.legacy_octal_numbers)
1029                {
1030                    return visitor.visit_i64(v);
1031                }
1032            } else {
1033                if let Ok(v) =
1034                    parse_int_unsigned::<u64>(t, "u64", location, self.cfg.legacy_octal_numbers)
1035                {
1036                    return visitor.visit_u64(v);
1037                }
1038            }
1039
1040            // Try float per YAML 1.2 forms, treating an f64-overflowing literal (e.g.
1041            // `1e999`) as non-finite too, so both shapes are handled consistently below.
1042            if let Some(v) = try_parse_float_incl_overflow(
1043                &effective,
1044                location,
1045                view.tag,
1046                self.cfg.angle_conversions,
1047            ) {
1048                return self.visit_typeless_float(v, view.raw.into_owned(), location, visitor);
1049            }
1050
1051            // Fallback: treat as string as-is, preserving zero-copy borrowing when the
1052            // parser handed us a slice of the original input.
1053            return match effective {
1054                Cow::Borrowed(b) => visitor.visit_borrowed_str(b),
1055                Cow::Owned(s) => visitor.visit_string(s),
1056            };
1057        }
1058
1059        match self.ev.peek()? {
1060            Some(Ev::SeqStart { .. }) => self.deserialize_seq(visitor),
1061            Some(Ev::MapStart { .. }) => self.deserialize_map(visitor),
1062            Some(Ev::RecursiveAlias { location, .. }) => {
1063                Err(Error::RecursiveReferencesRequireWeakTypes {
1064                    location: *location,
1065                })
1066            }
1067            Some(Ev::SeqEnd { location }) => Err(Error::UnexpectedSequenceEnd {
1068                location: *location,
1069            }),
1070            Some(Ev::MapEnd { location }) => Err(Error::UnexpectedMappingEnd {
1071                location: *location,
1072            }),
1073            None => {
1074                // When deserializing typeless positions (for example
1075                // `serde_json::Value`) exhausted input is treated as null.
1076                visitor.visit_unit()
1077            }
1078            Some(Ev::Taken { location }) => {
1079                Err(Error::unexpected("consumed event").with_location(*location))
1080            }
1081            Some(Ev::Scalar { location, .. }) => {
1082                Err(Error::unexpected("scalar").with_location(*location))
1083            }
1084        }
1085    }
1086
1087    /// Parse a boolean literal according to `strict_booleans`.
1088    ///
1089    /// Caller: Serde when target expects `bool`.
1090    /// Flow: scalar text → `Visitor::visit_bool`.
1091    fn deserialize_bool<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1092        let (s, tag, location) = self.take_scalar_cow_event()?;
1093        self.validate_scalar_tag(tag, SfTag::Bool, "boolean", location)?;
1094        let s = s.as_ref();
1095        let b: bool = if self.cfg.strict_booleans {
1096            parse_yaml12_bool(s).ok_or(Error::InvalidBooleanStrict { location })?
1097        } else {
1098            parse_yaml11_bool(s).ok_or(Error::InvalidScalar {
1099                ty: "boolean",
1100                location,
1101            })?
1102        };
1103        visitor.visit_bool(b)
1104    }
1105
1106    /// Parse a signed 8-bit integer.
1107    fn deserialize_i8<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1108        let (s, tag, location) = self.take_scalar_cow_event()?;
1109        self.validate_scalar_tag(tag, SfTag::Int, "i8", location)?;
1110        let v: i8 = parse_int_signed(s.as_ref(), "i8", location, self.cfg.legacy_octal_numbers)?;
1111        visitor.visit_i8(v)
1112    }
1113    /// Parse a signed 16-bit integer.
1114    fn deserialize_i16<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1115        let (s, tag, location) = self.take_scalar_cow_event()?;
1116        self.validate_scalar_tag(tag, SfTag::Int, "i16", location)?;
1117        let v: i16 = parse_int_signed(s.as_ref(), "i16", location, self.cfg.legacy_octal_numbers)?;
1118        visitor.visit_i16(v)
1119    }
1120    /// Parse a signed 32-bit integer.
1121    fn deserialize_i32<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1122        let (s, tag, location) = self.take_scalar_cow_event()?;
1123        self.validate_scalar_tag(tag, SfTag::Int, "i32", location)?;
1124        let v: i32 = parse_int_signed(s.as_ref(), "i32", location, self.cfg.legacy_octal_numbers)?;
1125        visitor.visit_i32(v)
1126    }
1127    /// Parse a signed 64-bit integer.
1128    fn deserialize_i64<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1129        let (s, tag, location) = self.take_scalar_cow_event()?;
1130        self.validate_scalar_tag(tag, SfTag::Int, "i64", location)?;
1131        let v: i64 = parse_int_signed(s.as_ref(), "i64", location, self.cfg.legacy_octal_numbers)?;
1132        visitor.visit_i64(v)
1133    }
1134    /// Parse a signed 128-bit integer.
1135    fn deserialize_i128<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1136        let (s, tag, location) = self.take_scalar_cow_event()?;
1137        self.validate_scalar_tag(tag, SfTag::Int, "i128", location)?;
1138        let v: i128 =
1139            parse_int_signed(s.as_ref(), "i128", location, self.cfg.legacy_octal_numbers)?;
1140        visitor.visit_i128(v)
1141    }
1142
1143    /// Parse an unsigned 8-bit integer.
1144    fn deserialize_u8<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1145        let (s, tag, location) = self.take_scalar_cow_event()?;
1146        self.validate_scalar_tag(tag, SfTag::Int, "u8", location)?;
1147        let v: u8 = parse_int_unsigned(s.as_ref(), "u8", location, self.cfg.legacy_octal_numbers)?;
1148        visitor.visit_u8(v)
1149    }
1150    /// Parse an unsigned 16-bit integer.
1151    fn deserialize_u16<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1152        let (s, tag, location) = self.take_scalar_cow_event()?;
1153        self.validate_scalar_tag(tag, SfTag::Int, "u16", location)?;
1154        let v: u16 =
1155            parse_int_unsigned(s.as_ref(), "u16", location, self.cfg.legacy_octal_numbers)?;
1156        visitor.visit_u16(v)
1157    }
1158    /// Parse an unsigned 32-bit integer.
1159    fn deserialize_u32<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1160        let (s, tag, location) = self.take_scalar_cow_event()?;
1161        self.validate_scalar_tag(tag, SfTag::Int, "u32", location)?;
1162        let v: u32 =
1163            parse_int_unsigned(s.as_ref(), "u32", location, self.cfg.legacy_octal_numbers)?;
1164        visitor.visit_u32(v)
1165    }
1166    /// Parse an unsigned 64-bit integer.
1167    fn deserialize_u64<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1168        let (s, tag, location) = self.take_scalar_cow_event()?;
1169        self.validate_scalar_tag(tag, SfTag::Int, "u64", location)?;
1170        let v: u64 =
1171            parse_int_unsigned(s.as_ref(), "u64", location, self.cfg.legacy_octal_numbers)?;
1172        visitor.visit_u64(v)
1173    }
1174    /// Parse an unsigned 128-bit integer.
1175    fn deserialize_u128<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1176        let (s, tag, location) = self.take_scalar_cow_event()?;
1177        self.validate_scalar_tag(tag, SfTag::Int, "u128", location)?;
1178        let v: u128 =
1179            parse_int_unsigned(s.as_ref(), "u128", location, self.cfg.legacy_octal_numbers)?;
1180        visitor.visit_u128(v)
1181    }
1182
1183    /// Parse a 32-bit float (supports YAML 1.2 `+.inf`, `-.inf`, `.nan`).
1184    fn deserialize_f32<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1185        let (s, tag, location) = self.take_scalar_cow_event()?;
1186        self.validate_scalar_tag(tag, SfTag::Float, "floating point", location)?;
1187        let v: f32 = parse_yaml12_float(s.as_ref(), location, tag, self.cfg.angle_conversions)?;
1188        visitor.visit_f32(v)
1189    }
1190    /// Parse a 64-bit float (supports YAML 1.2 `+.inf`, `-.inf`, `.nan`).
1191    fn deserialize_f64<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1192        let (s, tag, location) = self.take_scalar_cow_event()?;
1193        self.validate_scalar_tag(tag, SfTag::Float, "floating point", location)?;
1194        let v: f64 = parse_yaml12_float(s.as_ref(), location, tag, self.cfg.angle_conversions)?;
1195        visitor.visit_f64(v)
1196    }
1197
1198    /// Parse a single Unicode scalar value (`char`).
1199    ///
1200    /// Null semantics:
1201    /// - Tagged null or plain null-like scalars (empty, `~`, `null`, `Null`, or `NULL`) are not valid `char`.
1202    ///   Quoted forms are treated as normal strings and validated for length 1.
1203    /// - In `no_schema` mode, plain scalars that look like non-strings (numbers, bools, etc.)
1204    ///   must be quoted; this check uses scalar style to avoid flagging quoted scalars.
1205    fn deserialize_char<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1206        // Mirror deserialize_string pre-checks to leverage tag/style and maybe_not_string.
1207        if let Some(view) = self.peek_scalar_view()?
1208            && !view.tag.forces_string()
1209        {
1210            // Reject YAML null for char (allow quoted values like "null").
1211            if view.tag == SfTag::Null || scalar_is_nullish(&view.effective, &view.style) {
1212                let (_value, _tag, location) = self.take_scalar_event()?;
1213                return Err(Error::InvalidCharNull { location });
1214            } else if self.cfg.no_schema
1215                && maybe_not_string(&view.effective, &view.style, self.cfg.strict_booleans)
1216            {
1217                // Require quoting for ambiguous plain scalars in no_schema mode.
1218                let view = self.take_scalar_view()?;
1219                return Err(Self::quoting_required_for_scalar(&view));
1220            }
1221        }
1222
1223        // Now consume the scalar and validate it contains exactly one Unicode scalar value.
1224        let (s, tag, location) = self.take_scalar_cow_event()?;
1225        self.validate_scalar_tag(tag, SfTag::String, "char", location)?;
1226        let mut it = s.as_ref().chars();
1227        match (it.next(), it.next()) {
1228            (Some(c), None) => visitor.visit_char(c),
1229            _ => Err(Error::InvalidCharNotSingleScalar { location }),
1230        }
1231    }
1232
1233    /// Deserialize a borrowed string.
1234    ///
1235    /// When the scalar exists verbatim in the original input, this method uses
1236    /// `Visitor::visit_borrowed_str`.
1237    ///
1238    /// Borrowing is only possible for in-memory inputs (e.g. `from_str` / `from_slice`) and only
1239    /// when no transformation is required (no escape processing, folding, chomping/indent handling,
1240    /// or multi-line normalization).
1241    ///
1242    /// If borrowing is not possible, this method falls back to `Visitor::visit_string`.
1243    /// When the target type requires `&str`, that fallback produces a helpful error suggesting
1244    /// `String` or `Cow<str>`, with a [`TransformReason`] describing why borrowing was impossible.
1245    fn deserialize_str<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1246        let view = match self.peek_scalar_view()? {
1247            Some(view) => {
1248                // Check for null - not valid for string deserialization. A deliberately
1249                // substituted ${...} that resolved to "" stays a string, not null.
1250                if (view.tag == SfTag::Null
1251                    || (!view.interpolated && scalar_is_nullish(&view.effective, &view.style)))
1252                    && !view.tag.forces_string()
1253                {
1254                    let loc = view.location;
1255                    let _ = self.ev.next()?;
1256                    return Err(Error::NullIntoString { location: loc });
1257                } else if self.cfg.no_schema
1258                    && maybe_not_string(&view.effective, &view.style, self.cfg.strict_booleans)
1259                    && !view.tag.forces_string()
1260                {
1261                    let view = self.take_scalar_view()?;
1262                    return Err(Self::quoting_required_for_scalar(&view));
1263                }
1264
1265                if view.tag == SfTag::Binary && !self.cfg.ignore_binary_tag_for_string {
1266                    let res: Result<V::Value, Self::Error> =
1267                        visitor.visit_string(self.take_string_scalar()?);
1268                    return match res {
1269                        Ok(v) => Ok(v),
1270                        Err(err) if err.to_string().contains("expected a borrowed string") => Err(
1271                            Error::cannot_borrow_transformed(TransformReason::ParserReturnedOwned)
1272                                .with_location(view.location),
1273                        ),
1274                        Err(err) => Err(err),
1275                    };
1276                }
1277                if !(view.tag.can_parse_into_string()
1278                    || self.cfg.ignore_binary_tag_for_string && view.tag == SfTag::Binary)
1279                {
1280                    return Err(Error::TaggedScalarCannotDeserializeIntoString {
1281                        location: view.location,
1282                    });
1283                }
1284
1285                view
1286            }
1287            // Not a scalar: report what was found (or EOF), as `deserialize_string` does.
1288            None => {
1289                return Err(self
1290                    .take_scalar_view()
1291                    .err()
1292                    .unwrap_or_else(|| eof_with_loc(self.ev)));
1293            }
1294        };
1295
1296        let location = view.location;
1297        let redaction_ctx = view.redaction_ctx();
1298        let cannot_borrow_reason = if view.interpolated {
1299            TransformReason::VariableInterpolation
1300        } else if self.ev.input_for_borrowing().is_none() {
1301            TransformReason::InputNotBorrowable
1302        } else {
1303            TransformReason::ParserReturnedOwned
1304        };
1305
1306        let view = self.take_peeked_scalar_view(view)?;
1307        if let Cow::Borrowed(b) = view.effective {
1308            return visitor.visit_borrowed_str(b);
1309        }
1310
1311        let res: Result<V::Value, Self::Error> = with_scalar_redaction(redaction_ctx, || {
1312            visitor.visit_string(view.effective.into_owned())
1313        });
1314        match res {
1315            Ok(v) => Ok(v),
1316            Err(err) => {
1317                let msg = err.to_string();
1318                if msg.contains("expected a borrowed string") {
1319                    return Err(Error::cannot_borrow_transformed(cannot_borrow_reason)
1320                        .with_location(location));
1321                }
1322                Err(err)
1323            }
1324        }
1325    }
1326
1327    /// Deserialize an owned string (with `!!binary` UTF-8 support).
1328    ///
1329    /// Null semantics:
1330    /// - Tagged null or plain null-like scalars (empty, `~`, `null`, `Null`, or `NULL`) are not valid `String`.
1331    ///   Suggest using `Option<String>` for such YAML values.
1332    /// - Quoted "null" and quoted empty strings are treated as normal strings and allowed.
1333    ///
1334    /// **From/To:** scalar text (or base64-decoded bytes) → `Visitor::visit_string`.
1335    fn deserialize_string<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1336        // Reject YAML null when deserializing into String. Allow quoted forms.
1337        let view = if let Some(view) = self.peek_scalar_view()? {
1338            // If explicitly tagged as null, or plain null-like, this is not a valid String.
1339            // A ${...} that the user deliberately substituted to "" is a legitimate string.
1340            if (view.tag == SfTag::Null
1341                || (!view.interpolated && scalar_is_nullish(&view.effective, &view.style)))
1342                && !view.tag.forces_string()
1343            {
1344                // Consume the scalar to anchor the error at the correct location.
1345                let (_value, _tag, location) = self.take_scalar_event()?;
1346                return Err(Error::NullIntoString { location });
1347            } else if self.cfg.no_schema
1348                && maybe_not_string(&view.effective, &view.style, self.cfg.strict_booleans)
1349                && !view.tag.forces_string()
1350            {
1351                // Consume the scalar to anchor the error at the correct location.
1352                let view = self.take_scalar_view()?;
1353                return Err(Self::quoting_required_for_scalar(&view));
1354            }
1355            if view.tag == SfTag::Binary && !self.cfg.ignore_binary_tag_for_string {
1356                return visitor.visit_string(self.take_string_scalar()?);
1357            }
1358            if !(view.tag.can_parse_into_string()
1359                || self.cfg.ignore_binary_tag_for_string && view.tag == SfTag::Binary)
1360            {
1361                return Err(Error::TaggedScalarCannotDeserializeIntoString {
1362                    location: view.location,
1363                });
1364            }
1365
1366            view
1367        } else {
1368            // Let take_string_scalar handle the error if it's not a scalar
1369            return visitor.visit_string(self.take_string_scalar()?);
1370        };
1371
1372        let redaction_ctx = view.redaction_ctx();
1373        let view = self.take_peeked_scalar_view(view)?;
1374        match view.effective {
1375            Cow::Borrowed(b) => {
1376                with_scalar_redaction(redaction_ctx, || visitor.visit_borrowed_str(b))
1377            }
1378            Cow::Owned(s) => with_scalar_redaction(redaction_ctx, || visitor.visit_string(s)),
1379        }
1380    }
1381
1382    /// Deserialize bytes either from `!!binary` or from a sequence of integers (0..=255).
1383    ///
1384    /// **From/To:**
1385    /// - Tagged scalar → base64-decoded `Vec<u8>` into `Visitor::visit_byte_buf`.
1386    /// - Sequence of integers → packed into `Vec<u8>` and visited.
1387    fn deserialize_bytes<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1388        if let Some((_value, tag, _style, location)) = self.peek_effective_scalar()? {
1389            if tag == SfTag::Binary {
1390                let (value, data_location) = match self.ev.next()? {
1391                    Some(Ev::Scalar {
1392                        value, location, ..
1393                    }) => (value, location),
1394                    Some(other) => {
1395                        return Err(
1396                            Error::unexpected("binary scalar").with_location(other.location())
1397                        );
1398                    }
1399                    None => return Err(eof_with_loc(self.ev)),
1400                };
1401                let data =
1402                    decode_base64_yaml(&value).map_err(|err| err.with_location(data_location))?;
1403                return visitor.visit_byte_buf(data);
1404            }
1405            return Err(Error::BytesNotSupportedMissingBinaryTag { location });
1406        }
1407
1408        match self.ev.peek()? {
1409            // Untagged → expect a sequence of YAML integers (0..=255) and pack into bytes
1410            Some(Ev::SeqStart { .. }) => {
1411                self.expect_seq_start()?;
1412                let mut out = Vec::new();
1413                loop {
1414                    match self.ev.peek()? {
1415                        Some(Ev::SeqEnd { .. }) => {
1416                            let _ = self.ev.next()?; // consume end
1417                            break;
1418                        }
1419                        Some(_) => {
1420                            // Deserialize each element as u8 using our own Deser
1421                            let b: u8 = <u8 as serde_core::Deserialize>::deserialize(
1422                                YamlDeserializer::new(&mut *self.ev, self.cfg),
1423                            )?;
1424                            out.push(b);
1425                        }
1426                        None => return Err(eof_with_loc(self.ev)),
1427                    }
1428                }
1429                visitor.visit_byte_buf(out)
1430            }
1431
1432            // Anything else is unexpected here
1433            Some(other) => Err(
1434                Error::unexpected("scalar (!!binary) or sequence of 0..=255")
1435                    .with_location(other.location()),
1436            ),
1437            None => Err(eof_with_loc(self.ev)),
1438        }
1439    }
1440
1441    /// Deserialize owned bytes; same semantics as `deserialize_bytes`.
1442    fn deserialize_byte_buf<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
1443        self.deserialize_bytes(visitor)
1444    }
1445
1446    /// Deserialize an `Option<T>`.
1447    ///
1448    /// **What is treated as `None`?** End-of-input, container end, an explicitly
1449    /// tagged null, or a plain empty / `~` / `null` scalar permitting implicit null resolution.
1450    /// Explicit non-null core, binary, timestamp, and robotics tags are passed to the inner
1451    /// value's deserializer.
1452    fn deserialize_option<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1453        // Only when Serde asks for Option<T> do we interpret YAML null-like scalars as None.
1454        // Special-case for map keys: treat an explicit empty key captured as an empty mapping node
1455        // as None when the target is Option<T>. This is scoped strictly to key position to avoid
1456        // conflating a literal empty mapping `{}` with null for non-Option targets.
1457        if self.in_key && self.key_empty_map_node {
1458            // Recorded key is an empty mapping: treat as None for Option<T> in key position
1459            match self.ev.next()? {
1460                Some(Ev::MapStart { .. }) => {}
1461                Some(other) => {
1462                    return Err(
1463                        Error::unexpected("empty mapping start").with_location(other.location())
1464                    );
1465                }
1466                None => return Err(eof_with_loc(self.ev)),
1467            }
1468            match self.ev.next()? {
1469                Some(Ev::MapEnd { .. }) => {}
1470                Some(other) => {
1471                    return Err(
1472                        Error::unexpected("empty mapping end").with_location(other.location())
1473                    );
1474                }
1475                None => return Err(eof_with_loc(self.ev)),
1476            }
1477            return visitor.visit_none();
1478        }
1479
1480        if let Some((value, tag, style, _location)) = self.peek_effective_scalar()?
1481            && scalar_is_null(&tag, &value, &style)
1482        {
1483            let _ = self.ev.next()?; // consume the scalar
1484            return visitor.visit_none();
1485        }
1486
1487        match self.ev.peek()? {
1488            // End of input or an immediate container end both represent a missing value.
1489            None | Some(Ev::MapEnd { .. } | Ev::SeqEnd { .. }) => visitor.visit_none(),
1490
1491            // Otherwise there is a value → Some(...)
1492            Some(_) => visitor.visit_some(self),
1493        }
1494    }
1495
1496    /// Deserialize the unit type `()`.
1497    ///
1498    /// **What is “unit” here?** Rust's `()` indicates “no value”. In Serde it
1499    /// commonly appears in unit structs/variants or fields intentionally
1500    /// ignored.  
1501    /// **Accepted YAML forms:** end-of-input, container end, or a null-like
1502    /// scalar in plain style (`""`, `~`, `null`).
1503    fn deserialize_unit<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1504        if let Some((value, tag, style, _location)) = self.peek_effective_scalar()?
1505            && scalar_is_null(&tag, &value, &style)
1506        {
1507            let _ = self.ev.next()?; // consume the scalar
1508            return visitor.visit_unit();
1509        }
1510
1511        match self.ev.peek()? {
1512            // Treat absence or a container end where a value was expected as unit.
1513            None | Some(Ev::MapEnd { .. } | Ev::SeqEnd { .. }) => visitor.visit_unit(),
1514            // Anything else isn't a unit value
1515            Some(other) => Err(Error::UnexpectedValueForUnit {
1516                location: other.location(),
1517            }),
1518        }
1519    }
1520
1521    /// Deserialize a unit struct.
1522    ///
1523    /// **Delegation:** Struct unit forms are handled by allowing an **empty mapping**
1524    /// (`{}`) as the YAML representation, or by deferring to the same null-like
1525    /// forms accepted by `deserialize_unit`.  
1526    /// `Visitor` origin: Serde generates a visitor when
1527    /// deserializing the target unit struct type (via `derive(Deserialize)` or a
1528    /// manual impl). That visitor expects us to call `Visitor::visit_unit`.
1529    fn deserialize_unit_struct<V: Visitor<'de>>(
1530        self,
1531        _name: &'static str,
1532        visitor: V,
1533    ) -> Result<V::Value, Self::Error> {
1534        match self.ev.peek()? {
1535            // Allow empty mapping `{}` as a unit struct
1536            Some(Ev::MapStart { .. }) => {
1537                let _ = self.ev.next()?; // consume MapStart
1538                match self.ev.peek()? {
1539                    Some(Ev::MapEnd { .. }) => {
1540                        let _ = self.ev.next()?; // consume MapEnd
1541                        visitor.visit_unit()
1542                    }
1543                    Some(other) => Err(Error::ExpectedEmptyMappingForUnitStruct {
1544                        location: other.location(),
1545                    }),
1546                    None => Err(eof_with_loc(self.ev)),
1547                }
1548            }
1549            // Otherwise, delegate to unit handling (null, ~, empty scalar, EOF, etc.)
1550            _ => self.deserialize_unit(visitor),
1551        }
1552    }
1553
1554    /// Deserialize a newtype struct (`struct Wrapper(T);`) by delegating to its inner value.
1555    ///
1556    /// Why is this needed: Serde distinguishes *newtype structs* from their
1557    /// inner `T` so that attributes (like `#[serde(transparent)]`) and coherence
1558    /// rules are preserved. Even though YAML has no distinct “newtype” shape,
1559    /// Serde will invoke this method when the target is a newtype struct.  
1560    /// What do we do: Hand our own deserializer (`self`) to
1561    /// `Visitor::visit_newtype_struct`, which in turn will deserialize `T`
1562    /// using the same YAML event stream.
1563    fn deserialize_newtype_struct<V: Visitor<'de>>(
1564        mut self,
1565        n: &'static str,
1566        visitor: V,
1567    ) -> Result<V::Value, Self::Error> {
1568        match n {
1569            // Internal wrapper types use `__yaml_*` names (see `__yaml_rc_anchor`, etc.).
1570            "__yaml_spanned" => spanned_deser::deserialize_yaml_spanned(self, visitor),
1571            #[cfg(feature = "comments")]
1572            "__yaml_commented" => commented_deser::deserialize_yaml_commented(self, visitor),
1573            "__yaml_tagged" => tagged_deser::deserialize_yaml_tagged(self, visitor),
1574            "__yaml_rc_anchor" => {
1575                let anchor = self.peek_anchor_id()?;
1576                anchor_store::with_anchor_context(AnchorKind::Rc, anchor, || {
1577                    visitor.visit_newtype_struct(self)
1578                })
1579            }
1580            "__yaml_arc_anchor" => {
1581                let anchor = self.peek_anchor_id()?;
1582                anchor_store::with_anchor_context(AnchorKind::Arc, anchor, || {
1583                    visitor.visit_newtype_struct(self)
1584                })
1585            }
1586            "__yaml_rc_recursive" => {
1587                let anchor = self.peek_anchor_id()?;
1588                anchor_store::with_anchor_context(AnchorKind::RcRecursive, anchor, || {
1589                    visitor.visit_newtype_struct(self)
1590                })
1591            }
1592            "__yaml_arc_recursive" => {
1593                let anchor = self.peek_anchor_id()?;
1594                anchor_store::with_anchor_context(AnchorKind::ArcRecursive, anchor, || {
1595                    visitor.visit_newtype_struct(self)
1596                })
1597            }
1598            "__yaml_rc_weak_anchor" => {
1599                let anchor = self.peek_anchor_id()?;
1600                anchor_store::with_anchor_context(AnchorKind::Rc, anchor, || {
1601                    visitor.visit_newtype_struct(self)
1602                })
1603            }
1604            "__yaml_arc_weak_anchor" => {
1605                let anchor = self.peek_anchor_id()?;
1606                anchor_store::with_anchor_context(AnchorKind::Arc, anchor, || {
1607                    visitor.visit_newtype_struct(self)
1608                })
1609            }
1610            "__yaml_rc_recursion" => {
1611                let anchor = self.peek_anchor_id()?;
1612                anchor_store::with_anchor_context(AnchorKind::RcRecursive, anchor, || {
1613                    visitor.visit_newtype_struct(self)
1614                })
1615            }
1616            "__yaml_arc_recursion" => {
1617                let anchor = self.peek_anchor_id()?;
1618                anchor_store::with_anchor_context(AnchorKind::ArcRecursive, anchor, || {
1619                    visitor.visit_newtype_struct(self)
1620                })
1621            }
1622            _ => visitor.visit_newtype_struct(self),
1623        }
1624    }
1625
1626    /// Deserialize a YAML sequence into a Serde sequence.
1627    ///
1628    /// Flow: We provide a `SeqAccess` that repeatedly feeds nested
1629    /// `Deser` instances back into Serde for each element. Also supports a
1630    /// `!!binary` scalar as a byte *sequence* view when the caller expects a
1631    /// sequence of u8.
1632    fn deserialize_seq<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1633        if self.ev.take_empty_stream()? {
1634            return visitor.visit_seq(de::value::SeqDeserializer::<_, Error>::new(
1635                std::iter::empty::<()>(),
1636            ));
1637        }
1638        if let Some((_, SfTag::Binary, _, _)) = self.peek_effective_scalar()? {
1639            let (scalar, data_location) = match self.ev.next()? {
1640                Some(Ev::Scalar {
1641                    value, location, ..
1642                }) => (value, location),
1643                Some(other) => {
1644                    return Err(Error::unexpected("binary scalar").with_location(other.location()));
1645                }
1646                None => return Err(eof_with_loc(self.ev)),
1647            };
1648            let data =
1649                decode_base64_yaml(&scalar).map_err(|err| err.with_location(data_location))?;
1650            /// `SeqAccess` that iterates over bytes from a decoded `!!binary`.
1651            struct ByteSeq {
1652                data: Vec<u8>,
1653                idx: usize,
1654            }
1655            impl<'de> de::SeqAccess<'de> for ByteSeq {
1656                type Error = Error;
1657                fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Error>
1658                where
1659                    T: de::DeserializeSeed<'de>,
1660                {
1661                    if self.idx >= self.data.len() {
1662                        return Ok(None);
1663                    }
1664                    let b = self.data[self.idx];
1665                    self.idx += 1;
1666                    let deser = serde_core::de::value::U8Deserializer::<Error>::new(b);
1667                    seed.deserialize(deser).map(Some)
1668                }
1669            }
1670            return visitor.visit_seq(ByteSeq { data, idx: 0 });
1671        }
1672        // Comments passed in from the parent value slot belong to the first item
1673        // of the sequence. If this sequence is reached through a nested alias,
1674        // alias replay exposes the anchored sequence start here, so the same
1675        // rule applies to comments written above the alias token.
1676        let mut seq_start_comments = std::mem::take(&mut self.pending_value_comments);
1677        seq_start_comments.extend(self.ev.take_leading_comments_for_next_node()?);
1678        let seq_location = match self.ev.peek()? {
1679            Some(ev) => ev.location(),
1680            None => self.ev.last_location(),
1681        };
1682        let child_cfg = self.cfg.enter_container(seq_location)?;
1683        self.expect_seq_start()?;
1684        /// Streaming `SeqAccess` over the underlying `Events`.
1685        struct SA<'de, 'e> {
1686            ev: &'e mut dyn Events<'de>,
1687            cfg: Cfg,
1688            pending_first_element_comments: Vec<Cow<'de, str>>,
1689
1690            #[cfg(any(feature = "garde", feature = "validator"))]
1691            garde: Option<&'e mut PathRecorder>,
1692            #[cfg(any(feature = "garde", feature = "validator"))]
1693            idx: usize,
1694        }
1695        impl<'de> de::SeqAccess<'de> for SA<'de, '_> {
1696            type Error = Error;
1697            /// Produce the next element by recursively deserializing from the same event source.
1698            fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Error>
1699            where
1700                T: de::DeserializeSeed<'de>,
1701            {
1702                let (is_end, defined_location) = {
1703                    let peeked = self.ev.peek()?;
1704                    match peeked {
1705                        Some(Ev::SeqEnd { .. }) => (true, Location::UNKNOWN),
1706                        Some(ev) => (false, ev.location()),
1707                        None => return Err(eof_with_loc(self.ev)),
1708                    }
1709                };
1710
1711                if is_end {
1712                    return Ok(None);
1713                }
1714
1715                // The peek borrow is now released, so it's safe to query other cursor state.
1716                let reference_location = self.ev.reference_location();
1717                let alias_reference_location = self.ev.alias_reference_location();
1718                let _missing_field_guard = MissingFieldLocationGuard::new(reference_location);
1719                let mut item_comments = std::mem::take(&mut self.pending_first_element_comments);
1720                item_comments.extend(self.ev.take_leading_comments_for_next_node()?);
1721                let value_separator_comments = self
1722                    .ev
1723                    .take_separator_comments_before_sequence_item_value()?;
1724
1725                #[cfg(any(feature = "garde", feature = "validator"))]
1726                {
1727                    if let Some(garde_ref) = self.garde.as_mut() {
1728                        let recorder: &mut PathRecorder = garde_ref;
1729
1730                        let prev = recorder.current.take();
1731                        let now = prev.clone().join(self.idx);
1732                        recorder.current = now.clone();
1733                        recorder.map.insert(
1734                            now,
1735                            Locations {
1736                                reference_location,
1737                                defined_location,
1738                            },
1739                        );
1740
1741                        let mut de =
1742                            YamlDeserializer::new_with_path_recorder(self.ev, self.cfg, recorder);
1743                        de.pending_comments = item_comments;
1744                        de.pending_value_separator_comments = value_separator_comments;
1745                        let redaction_ctx = de.peek_scalar_redaction_ctx()?;
1746                        let res = with_subtree_redaction(redaction_ctx, || seed.deserialize(de))
1747                            .map(Some)
1748                            .map_err(|e| {
1749                                attach_alias_locations_if_missing(
1750                                    e,
1751                                    alias_reference_location,
1752                                    defined_location,
1753                                )
1754                            });
1755
1756                        recorder.current = prev;
1757                        self.idx += 1;
1758                        return res;
1759                    }
1760                }
1761
1762                let mut de = YamlDeserializer::new(self.ev, self.cfg);
1763                de.pending_comments = item_comments;
1764                de.pending_value_separator_comments = value_separator_comments;
1765                let redaction_ctx = de.peek_scalar_redaction_ctx()?;
1766                with_subtree_redaction(redaction_ctx, || seed.deserialize(de))
1767                    .map(Some)
1768                    .map_err(|e| {
1769                        attach_alias_locations_if_missing(
1770                            e,
1771                            alias_reference_location,
1772                            defined_location,
1773                        )
1774                    })
1775            }
1776        }
1777
1778        #[cfg(any(feature = "garde", feature = "validator"))]
1779        let garde = self.garde;
1780
1781        let result = visitor.visit_seq(SA {
1782            ev: self.ev,
1783            cfg: child_cfg,
1784            pending_first_element_comments: seq_start_comments,
1785
1786            #[cfg(any(feature = "garde", feature = "validator"))]
1787            garde,
1788            #[cfg(any(feature = "garde", feature = "validator"))]
1789            idx: 0,
1790        })?;
1791        drain_remaining_sequence(self.ev)?;
1792        Ok(result)
1793    }
1794
1795    /// Deserialize a tuple; identical mechanics to sequences (fixed length checked by caller).
1796    fn deserialize_tuple<V: Visitor<'de>>(
1797        self,
1798        len: usize,
1799        visitor: V,
1800    ) -> Result<V::Value, Self::Error> {
1801        self.deserialize_seq(TupleLenVisitor {
1802            inner: visitor,
1803            len,
1804        })
1805    }
1806
1807    /// Deserialize a tuple struct; identical mechanics to sequences.
1808    fn deserialize_tuple_struct<V: Visitor<'de>>(
1809        self,
1810        _name: &'static str,
1811        len: usize,
1812        visitor: V,
1813    ) -> Result<V::Value, Self::Error> {
1814        self.deserialize_seq(TupleLenVisitor {
1815            inner: visitor,
1816            len,
1817        })
1818    }
1819
1820    /// Deserialize a YAML mapping into a Serde map/struct field stream.
1821    ///
1822    /// Flow: We expose a `MapAccess` implementation (`MA`) that:
1823    /// - Captures key/value nodes (able to replay them),
1824    /// - Applies duplicate-key policy,
1825    /// - Expands YAML merge keys (`<<`) in the correct precedence order.
1826    ///
1827    /// Caller: Serde field visitors for maps and for Rust structs
1828    /// (which Serde also requests via `deserialize_map`).
1829    fn deserialize_map<V: Visitor<'de>>(mut self, visitor: V) -> Result<V::Value, Self::Error> {
1830        if self.ev.take_empty_stream()? {
1831            struct EmptyMap {
1832                location: Location,
1833            }
1834            impl<'de> de::MapAccess<'de> for EmptyMap {
1835                type Error = Error;
1836
1837                fn next_key_seed<K>(&mut self, _seed: K) -> Result<Option<K::Value>, Error>
1838                where
1839                    K: de::DeserializeSeed<'de>,
1840                {
1841                    Ok(None)
1842                }
1843
1844                fn next_value_seed<V>(&mut self, _seed: V) -> Result<V::Value, Error>
1845                where
1846                    V: de::DeserializeSeed<'de>,
1847                {
1848                    Err(Error::ValueRequestedBeforeKey {
1849                        location: self.location,
1850                    })
1851                }
1852            }
1853            return visitor.visit_map(EmptyMap {
1854                location: self.ev.last_location(),
1855            });
1856        }
1857        // Same-line separator comments on the parent field belong to this mapping node.
1858        // `Commented<Container>` consumes those comments before this point; a plain map
1859        // must not reattach them to the first child key.
1860        let _map_node_comments = std::mem::take(&mut self.pending_value_separator_comments);
1861        // Comments already inside the map, before the first key, remain first-key
1862        // comments. `Commented<Container>` defers these instead of capturing them.
1863        // If this map is reached through a nested alias, alias replay exposes the
1864        // anchored map start here; comments above the alias follow the same rule.
1865        let mut map_start_comments = std::mem::take(&mut self.pending_value_comments);
1866        map_start_comments.extend(self.ev.take_leading_comments_for_next_node()?);
1867        let map_location = match self.ev.peek()? {
1868            Some(ev) => ev.location(),
1869            None => self.ev.last_location(),
1870        };
1871        let map_reference_location = self.ev.reference_location();
1872        let child_cfg = self.cfg.enter_container(map_location)?;
1873        self.expect_map_start()?;
1874
1875        // Ensure "missing field" errors (which have no natural span) get attributed to the
1876        // current container.
1877        let _missing_field_guard = MissingFieldLocationGuard::new(map_reference_location);
1878
1879        #[cfg(any(feature = "garde", feature = "validator"))]
1880        if let Some(recorder) = self.garde.as_mut() {
1881            // Record the container itself, not just its leaf scalars, so that missing-field
1882            // errors can fall back to a parent structure.
1883            let path = recorder.current.clone();
1884            recorder.map.insert(
1885                path,
1886                Locations {
1887                    reference_location: map_reference_location,
1888                    defined_location: map_location,
1889                },
1890            );
1891        }
1892
1893        fn collect_last_wins_entries<'de>(
1894            ev: &mut dyn Events<'de>,
1895            mut first_key_comments: Vec<Cow<'de, str>>,
1896            cfg: Cfg,
1897            mut first_key: Option<KeyNode<'de>>,
1898            prior_seen: &HashSet<KeyFingerprint<'de>>,
1899            mut merge_batches: VecDeque<Vec<PendingEntry<'de>>>,
1900        ) -> Result<VecDeque<PendingEntry<'de>>, Error> {
1901            with_deferred_recursive_aliases(ev, |ev| {
1902                let mut explicit_entries = Vec::new();
1903
1904                loop {
1905                    let (key, field_comments) = if let Some(key) = first_key.take() {
1906                        (key, std::mem::take(&mut first_key_comments))
1907                    } else {
1908                        match ev.peek()? {
1909                            Some(Ev::MapEnd { .. }) => {
1910                                let _ = ev.next()?;
1911                                break;
1912                            }
1913                            Some(_) => {
1914                                let mut key_comments = std::mem::take(&mut first_key_comments);
1915                                key_comments.extend(ev.take_leading_comments_for_next_node()?);
1916                                let key =
1917                                    capture_node_with_legacy_octal(ev, cfg.legacy_octal_numbers)?;
1918                                (key, key_comments)
1919                            }
1920                            None => return Err(eof_with_loc(ev)),
1921                        }
1922                    };
1923
1924                    if is_merge_key(&key) {
1925                        match cfg.merge_keys {
1926                            MergeKeyPolicy::Merge => {
1927                                let _ = ev.peek()?;
1928                                let merge_ref_loc = ev.reference_location();
1929                                let entries =
1930                                    pending_entries_from_live_events(ev, merge_ref_loc, cfg)?;
1931                                if !entries.is_empty() {
1932                                    merge_batches.push_back(entries);
1933                                }
1934                                continue;
1935                            }
1936                            MergeKeyPolicy::AsOrdinary => {}
1937                            MergeKeyPolicy::Error => {
1938                                return Err(Error::MergeKeyNotAllowed {
1939                                    location: key.location(),
1940                                });
1941                            }
1942                        }
1943                    }
1944
1945                    if cfg.reject_tagged_field_names {
1946                        key.validate_field_name_tag()?;
1947                    }
1948                    let value_separator_comments =
1949                        ev.take_separator_comments_before_mapping_value()?;
1950                    let value_comments = ev.take_leading_comments_for_next_node()?;
1951                    // Comment hooks need not fill lookahead, so locate the value explicitly
1952                    // before recording its use site for replay.
1953                    let _ = ev.peek()?;
1954                    let reference_location = ev.reference_location();
1955                    let value = capture_node_with_legacy_octal(ev, cfg.legacy_octal_numbers)?;
1956                    explicit_entries.push(PendingEntry {
1957                        key,
1958                        value,
1959                        reference_location,
1960                        field_comments,
1961                        value_separator_comments,
1962                        value_comments,
1963                    });
1964                }
1965
1966                let mut explicit_entries = apply_duplicate_key_policy_to_entries(
1967                    explicit_entries,
1968                    cfg.dup_policy,
1969                    cfg.merge_keys,
1970                )?;
1971                let mut seen = prior_seen.clone();
1972                seen.reserve(explicit_entries.len());
1973                for entry in &explicit_entries {
1974                    seen.insert(entry.key.fingerprint().into_owned());
1975                }
1976
1977                let mut merge_entries = Vec::new();
1978                for batch in merge_batches {
1979                    for entry in batch {
1980                        let fingerprint = entry.key.fingerprint().into_owned();
1981                        if seen.insert(fingerprint) {
1982                            merge_entries.push(entry);
1983                        }
1984                    }
1985                }
1986
1987                explicit_entries.extend(merge_entries);
1988                Ok(explicit_entries.into_iter().collect())
1989            })
1990        }
1991
1992        /// Streaming `MapAccess` over the underlying `Events`.
1993        struct MA<'de, 'e> {
1994            ev: &'e mut dyn Events<'de>,
1995            cfg: Cfg,
1996            struct_mode: bool,
1997            have_key: bool,
1998
1999            // Persist a best-effort “current location” across `next_key_seed` returning to Serde.
2000            // This allows Serde-produced structural/type errors (e.g. `unknown_field`) to carry
2001            // a useful span even though they are raised outside of this deserializer’s call stack.
2002            fallback_guard: Option<MissingFieldLocationGuard>,
2003
2004            #[cfg(any(feature = "garde", feature = "validator"))]
2005            garde: Option<&'e mut PathRecorder>,
2006            #[cfg(any(feature = "garde", feature = "validator"))]
2007            pending_path_segment: Option<String>,
2008
2009            // For duplicate-key detection for arbitrary keys.
2010            seen: HashSet<KeyFingerprint<'de>>,
2011            pending: VecDeque<PendingEntry<'de>>,
2012            merge_stack: VecDeque<Vec<PendingEntry<'de>>>,
2013            flushing_merges: bool,
2014            live_done: bool,
2015            pending_value: Option<Box<(RecordedEvents<'de>, Location)>>,
2016            pending_field_comments: Vec<Cow<'de, str>>,
2017            pending_value_separator_comments: Vec<Cow<'de, str>>,
2018            pending_value_comments: Vec<Cow<'de, str>>,
2019            pending_first_key_comments: Vec<Cow<'de, str>>,
2020        }
2021
2022        impl<'de> MA<'de, '_> {
2023            /// Skip exactly one YAML node (scalar/sequence/mapping) in the live stream.
2024            ///
2025            /// Used by:
2026            /// - `DuplicateKeyPolicy::FirstWins` to discard a later value.
2027            fn skip_one_node(&mut self) -> Result<(), Error> {
2028                if self.cfg.merge_keys == MergeKeyPolicy::Error {
2029                    let node =
2030                        capture_node_with_legacy_octal(self.ev, self.cfg.legacy_octal_numbers)?;
2031                    return validate_no_merge_keys_in_node_events(node.events());
2032                }
2033
2034                let mut depth; // assigned later
2035                match self.ev.next()? {
2036                    Some(Ev::RecursiveAlias { location, .. }) => {
2037                        return Err(Error::RecursiveReferencesRequireWeakTypes { location });
2038                    }
2039                    Some(Ev::Scalar { .. }) => return Ok(()),
2040                    Some(Ev::SeqStart { .. } | Ev::MapStart { .. }) => depth = 1,
2041                    Some(Ev::SeqEnd { location } | Ev::MapEnd { location }) => {
2042                        return Err(Error::UnexpectedContainerEndWhileSkippingNode { location });
2043                    }
2044                    Some(Ev::Taken { location }) => {
2045                        return Err(Error::unexpected("consumed event").with_location(location));
2046                    }
2047                    None => return Err(eof_with_loc(self.ev)),
2048                }
2049                while depth != 0 {
2050                    match self.ev.next()? {
2051                        Some(Ev::RecursiveAlias { location, .. }) => {
2052                            return Err(Error::RecursiveReferencesRequireWeakTypes { location });
2053                        }
2054                        Some(Ev::SeqStart { .. } | Ev::MapStart { .. }) => depth += 1,
2055                        Some(Ev::SeqEnd { .. } | Ev::MapEnd { .. }) => depth -= 1,
2056                        Some(Ev::Scalar { .. }) => {}
2057                        Some(Ev::Taken { location }) => {
2058                            return Err(Error::unexpected("consumed event").with_location(location));
2059                        }
2060                        None => return Err(eof_with_loc(self.ev)),
2061                    }
2062                }
2063                Ok(())
2064            }
2065
2066            fn validate_skipped_node(&self, node: &KeyNode<'_>) -> Result<(), Error> {
2067                if self.cfg.merge_keys == MergeKeyPolicy::Error {
2068                    validate_no_merge_keys_in_node_events(node.events())?;
2069                }
2070                Ok(())
2071            }
2072
2073            /// Deserialize a recorded key using a temporary `ReplayEvents`.
2074            ///
2075            /// Arguments:
2076            /// - `seed`: Serde seed for the key type.
2077            /// - `events`: recorded node events for the key.
2078            fn deserialize_recorded_key<'de2, K>(
2079                &mut self,
2080                seed: K,
2081                events: RecordedEvents<'de2>,
2082                kemn: bool,
2083            ) -> Result<K::Value, Error>
2084            where
2085                K: de::DeserializeSeed<'de2>,
2086            {
2087                let mut replay = ReplayEvents::new(
2088                    events,
2089                    #[cfg(feature = "properties")]
2090                    self.ev.property_interpolation().for_key(),
2091                );
2092
2093                // Get location from replay events for error reporting.
2094                let location = replay.reference_location();
2095
2096                let de = YamlDeserializer::<'de2, '_> {
2097                    ev: &mut replay,
2098                    cfg: self.cfg,
2099                    in_key: true,
2100                    struct_mode: false,
2101                    key_empty_map_node: kemn,
2102                    pending_comments: Vec::new(),
2103                    pending_value_separator_comments: Vec::new(),
2104                    pending_value_comments: Vec::new(),
2105
2106                    #[cfg(any(feature = "garde", feature = "validator"))]
2107                    garde: None,
2108                };
2109                seed.deserialize(de).map_err(|e| {
2110                    if e.location().is_none() {
2111                        e.with_location(location)
2112                    } else {
2113                        e
2114                    }
2115                })
2116            }
2117
2118            /// Push a batch of entries to the front of the pending queue in order.
2119            fn enqueue_entries(&mut self, entries: Vec<PendingEntry<'de>>) {
2120                self.pending.reserve(entries.len());
2121                for entry in entries.into_iter().rev() {
2122                    self.pending.push_front(entry);
2123                }
2124            }
2125
2126            /// Pop the next merge batch and enqueue its entries; return whether anything was queued.
2127            fn enqueue_next_merge_batch(&mut self) -> bool {
2128                while let Some(entries) = self.merge_stack.pop_front() {
2129                    if entries.is_empty() {
2130                        continue;
2131                    }
2132                    self.enqueue_entries(entries);
2133                    return true;
2134                }
2135                false
2136            }
2137
2138            /// Consume this mapping's end if its visitor returned without asking for it.
2139            ///
2140            /// typetag's externally tagged visitor reads its one key/value pair
2141            /// and returns without polling `next_key` again. Poll once on the visitor's behalf so
2142            /// the closing event is consumed, while still rejecting an unexpected extra entry.
2143            fn finish(&mut self) -> Result<(), Error> {
2144                if self.have_key {
2145                    return Err(
2146                        Error::unexpected("mapping end").with_location(self.ev.last_location())
2147                    );
2148                }
2149
2150                if de::MapAccess::next_key::<de::IgnoredAny>(self)?.is_some() {
2151                    return Err(
2152                        Error::unexpected("mapping end").with_location(self.ev.last_location())
2153                    );
2154                }
2155
2156                Ok(())
2157            }
2158        }
2159
2160        impl<'de> de::MapAccess<'de> for MA<'de, '_> {
2161            type Error = Error;
2162
2163            /// Produce the next key for the visitor, honoring duplicate policy and merges.
2164            fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Error>
2165            where
2166                K: de::DeserializeSeed<'de>,
2167            {
2168                let mut seed = Some(seed);
2169
2170                loop {
2171                    if let Some(entry) = self.pending.pop_front() {
2172                        let PendingEntry {
2173                            mut key,
2174                            mut value,
2175                            reference_location,
2176                            field_comments,
2177                            value_separator_comments,
2178                            value_comments,
2179                        } = entry;
2180                        let fingerprint = key.take_fingerprint();
2181                        let location = key.location();
2182
2183                        let is_duplicate = self.seen.contains(&fingerprint);
2184                        if self.flushing_merges {
2185                            if is_duplicate {
2186                                self.validate_skipped_node(&value)?;
2187                                continue;
2188                            }
2189                        } else {
2190                            match self.cfg.dup_policy {
2191                                DuplicateKeyPolicy::Error => {
2192                                    if is_duplicate {
2193                                        let key = key.stringy_scalar_value().map(|s| s.to_owned());
2194                                        return Err(Error::DuplicateMappingKey { key, location });
2195                                    }
2196                                }
2197                                DuplicateKeyPolicy::FirstWins => {
2198                                    if is_duplicate {
2199                                        self.validate_skipped_node(&value)?;
2200                                        continue;
2201                                    }
2202                                }
2203                                DuplicateKeyPolicy::LastWins | DuplicateKeyPolicy::PassThrough => {}
2204                            }
2205                        }
2206
2207                        #[cfg(any(feature = "garde", feature = "validator"))]
2208                        let key_path_segment = key.stringy_scalar_value().map(ToOwned::to_owned);
2209                        let events = key.take_events();
2210                        let value_events = value.take_events();
2211                        let kemn = is_empty_mapping_key_fingerprint(&fingerprint);
2212                        let Some(key_seed) = seed.take() else {
2213                            return Err(Error::InternalSeedReusedForMapKey { location });
2214                        };
2215
2216                        // Set the fallback location to the key span *before* deserializing the key.
2217                        // Serde can raise `unknown_field` (and similar structural errors) during key
2218                        // deserialization itself; if we set the guard only after deserialization,
2219                        // those errors will incorrectly fall back to the container start.
2220                        match &mut self.fallback_guard {
2221                            Some(guard) => guard.replace_location(location),
2222                            None => {
2223                                self.fallback_guard =
2224                                    Some(MissingFieldLocationGuard::new(location));
2225                            }
2226                        }
2227
2228                        let key_value = self.deserialize_recorded_key(key_seed, events, kemn)?;
2229                        self.have_key = true;
2230                        self.pending_field_comments = field_comments;
2231                        self.pending_value_separator_comments = value_separator_comments;
2232                        self.pending_value_comments = value_comments;
2233                        self.pending_value = Some(Box::new((value_events, reference_location)));
2234
2235                        #[cfg(any(feature = "garde", feature = "validator"))]
2236                        {
2237                            self.pending_path_segment = key_path_segment;
2238                        }
2239
2240                        if remembers_seen_keys(&self.cfg) {
2241                            self.seen.insert(fingerprint);
2242                        }
2243                        return Ok(Some(key_value));
2244                    }
2245
2246                    if self.live_done {
2247                        return Ok(None);
2248                    }
2249
2250                    if self.flushing_merges {
2251                        if self.enqueue_next_merge_batch() {
2252                            continue;
2253                        }
2254                        self.flushing_merges = false;
2255                        self.live_done = true;
2256                        return Ok(None);
2257                    }
2258
2259                    match self.ev.peek()? {
2260                        Some(Ev::MapEnd { .. }) => {
2261                            let _ = self.ev.next()?; // consume end
2262                            if self.merge_stack.is_empty() {
2263                                self.live_done = true;
2264                                return Ok(None);
2265                            }
2266                            self.flushing_merges = true;
2267                            if self.enqueue_next_merge_batch() {
2268                                continue;
2269                            }
2270                            self.flushing_merges = false;
2271                            self.live_done = true;
2272                            return Ok(None);
2273                        }
2274                        Some(_) => {
2275                            let mut key_comments =
2276                                std::mem::take(&mut self.pending_first_key_comments);
2277                            key_comments.extend(self.ev.take_leading_comments_for_next_node()?);
2278                            let mut key_node = capture_node_with_legacy_octal(
2279                                self.ev,
2280                                self.cfg.legacy_octal_numbers,
2281                            )?;
2282                            if is_merge_key(&key_node) {
2283                                match self.cfg.merge_keys {
2284                                    MergeKeyPolicy::Merge => {
2285                                        // Preserve where the merge value is *referenced* (use-site).
2286                                        // For alias merges (`<<: *m`), `key_node`/`value_node`
2287                                        // locations will point at the anchored mapping, but we want
2288                                        // `referenced` to point at the alias token.
2289                                        let _ = self.ev.peek()?;
2290                                        let merge_ref_loc = self.ev.reference_location();
2291                                        let entries = pending_entries_from_live_events(
2292                                            self.ev,
2293                                            merge_ref_loc,
2294                                            Cfg {
2295                                                reject_tagged_field_names: self.struct_mode
2296                                                    && self.cfg.reject_tagged_field_names,
2297                                                ..self.cfg
2298                                            },
2299                                        )?;
2300                                        if !entries.is_empty() {
2301                                            self.merge_stack.push_back(entries);
2302                                        }
2303                                        continue;
2304                                    }
2305                                    MergeKeyPolicy::AsOrdinary => {}
2306                                    MergeKeyPolicy::Error => {
2307                                        return Err(Error::MergeKeyNotAllowed {
2308                                            location: key_node.location(),
2309                                        });
2310                                    }
2311                                }
2312                            }
2313
2314                            if self.struct_mode && self.cfg.reject_tagged_field_names {
2315                                key_node.validate_field_name_tag()?;
2316                            }
2317
2318                            if matches!(self.cfg.dup_policy, DuplicateKeyPolicy::LastWins)
2319                                && key_node.fingerprint().contains_integer()
2320                            {
2321                                // Integer identity can differ from the key's spelling in the
2322                                // target map, so select the winning YAML entries before Serde.
2323                                self.pending = collect_last_wins_entries(
2324                                    self.ev,
2325                                    key_comments,
2326                                    Cfg {
2327                                        reject_tagged_field_names: self.struct_mode
2328                                            && self.cfg.reject_tagged_field_names,
2329                                        ..self.cfg
2330                                    },
2331                                    Some(key_node),
2332                                    &self.seen,
2333                                    std::mem::take(&mut self.merge_stack),
2334                                )?;
2335                                self.live_done = true;
2336                                continue;
2337                            }
2338
2339                            let fingerprint = key_node.fingerprint();
2340                            let is_duplicate = self.seen.contains(&fingerprint);
2341                            match self.cfg.dup_policy {
2342                                DuplicateKeyPolicy::Error => {
2343                                    if is_duplicate {
2344                                        let location = key_node.location();
2345                                        let key =
2346                                            key_node.stringy_scalar_value().map(|s| s.to_owned());
2347                                        return Err(Error::DuplicateMappingKey { key, location });
2348                                    }
2349                                }
2350                                DuplicateKeyPolicy::FirstWins => {
2351                                    if is_duplicate {
2352                                        self.skip_one_node()?;
2353                                        continue;
2354                                    }
2355                                }
2356                                DuplicateKeyPolicy::LastWins | DuplicateKeyPolicy::PassThrough => {}
2357                            }
2358
2359                            let kemn_direct =
2360                                is_empty_mapping_key_fingerprint(fingerprint.as_ref());
2361                            // Deserialize the key from recorded events and read the value live.
2362                            let fingerprint =
2363                                remembers_seen_keys(&self.cfg).then(|| fingerprint.into_owned());
2364                            let location = key_node.location();
2365                            #[cfg(any(feature = "garde", feature = "validator"))]
2366                            let key_path_segment =
2367                                key_node.stringy_scalar_value().map(ToOwned::to_owned);
2368                            let events = key_node.take_events();
2369                            let Some(key_seed) = seed.take() else {
2370                                return Err(Error::InternalSeedReusedForMapKey { location });
2371                            };
2372
2373                            // Same reasoning as the buffered path above: set key-span fallback
2374                            // before key deserialization so errors during key parsing are
2375                            // attributed to this key.
2376                            match &mut self.fallback_guard {
2377                                Some(guard) => guard.replace_location(location),
2378                                None => {
2379                                    self.fallback_guard =
2380                                        Some(MissingFieldLocationGuard::new(location));
2381                                }
2382                            }
2383
2384                            let key_value =
2385                                self.deserialize_recorded_key(key_seed, events, kemn_direct)?;
2386                            self.have_key = true;
2387                            self.pending_field_comments = key_comments;
2388                            self.pending_value = None; // value will be read live
2389
2390                            #[cfg(any(feature = "garde", feature = "validator"))]
2391                            {
2392                                self.pending_path_segment = key_path_segment;
2393                            }
2394
2395                            if let Some(fingerprint) = fingerprint {
2396                                self.seen.insert(fingerprint);
2397                            }
2398                            return Ok(Some(key_value));
2399                        }
2400                        None => return Err(eof_with_loc(self.ev)),
2401                    }
2402                }
2403            }
2404
2405            /// Provide the value corresponding to the most recently yielded key.
2406            fn next_value_seed<Vv>(&mut self, seed: Vv) -> Result<Vv::Value, Error>
2407            where
2408                Vv: de::DeserializeSeed<'de>,
2409            {
2410                if !self.have_key {
2411                    return Err(Error::ValueRequestedBeforeKey {
2412                        location: self.ev.last_location(),
2413                    });
2414                }
2415                self.have_key = false;
2416
2417                #[cfg(any(feature = "garde", feature = "validator"))]
2418                let pending_segment = self.pending_path_segment.take();
2419
2420                let field_comments = std::mem::take(&mut self.pending_field_comments);
2421                let mut value_separator_comments =
2422                    std::mem::take(&mut self.pending_value_separator_comments);
2423                let mut value_comments = std::mem::take(&mut self.pending_value_comments);
2424
2425                if let Some(events) = self.pending_value.take() {
2426                    let (events, fallback_reference_location) = *events;
2427                    // Each captured node retains its own origin. Fixing the reference to
2428                    // the outer value would mistake ordinary descendants for aliases.
2429                    let mut replay = ReplayEvents::new(
2430                        events,
2431                        #[cfg(feature = "properties")]
2432                        self.ev.property_interpolation().clone(),
2433                    );
2434
2435                    // Definition-site location: where the node is defined in the YAML.
2436                    // For aliases, this will point at the anchor definition.
2437                    let defined_location = match replay.peek()? {
2438                        Some(ev) => ev.location(),
2439                        None => replay.last_location(),
2440                    };
2441                    let alias_reference_location = replay.alias_reference_location();
2442                    #[cfg(any(feature = "garde", feature = "validator"))]
2443                    let reference_location = if defined_location == Location::UNKNOWN {
2444                        fallback_reference_location
2445                    } else {
2446                        replay.reference_location()
2447                    };
2448                    #[cfg(not(any(feature = "garde", feature = "validator")))]
2449                    let _ = fallback_reference_location;
2450
2451                    #[cfg(any(feature = "garde", feature = "validator"))]
2452                    {
2453                        if let (Some(seg), Some(garde_ref)) = (pending_segment, self.garde.as_mut())
2454                        {
2455                            let recorder: &mut PathRecorder = garde_ref;
2456
2457                            let prev = recorder.current.take();
2458                            let now = prev.clone().join(seg.as_str());
2459                            recorder.current = now.clone();
2460                            recorder.map.insert(
2461                                now,
2462                                Locations {
2463                                    reference_location,
2464                                    defined_location,
2465                                },
2466                            );
2467
2468                            let mut de = YamlDeserializer::new_with_path_recorder(
2469                                &mut replay,
2470                                self.cfg,
2471                                recorder,
2472                            );
2473                            de.pending_comments = field_comments;
2474                            de.pending_value_separator_comments = value_separator_comments;
2475                            de.pending_value_comments = value_comments;
2476                            let redaction_ctx = de.peek_scalar_redaction_ctx()?;
2477                            let res =
2478                                with_subtree_redaction(redaction_ctx, || seed.deserialize(de))
2479                                    .map_err(|e| {
2480                                        attach_alias_locations_if_missing(
2481                                            e,
2482                                            alias_reference_location,
2483                                            defined_location,
2484                                        )
2485                                    });
2486                            recorder.current = prev;
2487                            return res;
2488                        }
2489                    }
2490
2491                    let mut de = YamlDeserializer::new(&mut replay, self.cfg);
2492                    de.pending_comments = field_comments;
2493                    de.pending_value_separator_comments = value_separator_comments;
2494                    de.pending_value_comments = value_comments;
2495                    let redaction_ctx = de.peek_scalar_redaction_ctx()?;
2496                    with_subtree_redaction(redaction_ctx, || seed.deserialize(de)).map_err(|e| {
2497                        attach_alias_locations_if_missing(
2498                            e,
2499                            alias_reference_location,
2500                            defined_location,
2501                        )
2502                    })
2503                } else {
2504                    value_separator_comments
2505                        .extend(self.ev.take_separator_comments_before_mapping_value()?);
2506                    value_comments.extend(self.ev.take_leading_comments_for_next_node()?);
2507
2508                    // Live stream: get both locations for potential alias error reporting.
2509                    let defined_location = match self.ev.peek()? {
2510                        Some(ev) => ev.location(),
2511                        None => self.ev.last_location(),
2512                    };
2513
2514                    #[cfg(any(feature = "garde", feature = "validator"))]
2515                    let reference_location = self.ev.reference_location();
2516                    let alias_reference_location = self.ev.alias_reference_location();
2517
2518                    #[cfg(any(feature = "garde", feature = "validator"))]
2519                    {
2520                        if let (Some(seg), Some(garde_ref)) = (pending_segment, self.garde.as_mut())
2521                        {
2522                            let recorder: &mut PathRecorder = garde_ref;
2523                            let prev = recorder.current.take();
2524                            let now = prev.clone().join(seg.as_str());
2525                            recorder.current = now.clone();
2526                            recorder.map.insert(
2527                                now,
2528                                Locations {
2529                                    reference_location,
2530                                    defined_location,
2531                                },
2532                            );
2533
2534                            let mut de = YamlDeserializer::new_with_path_recorder(
2535                                self.ev, self.cfg, recorder,
2536                            );
2537                            de.pending_comments = field_comments;
2538                            de.pending_value_separator_comments = value_separator_comments;
2539                            de.pending_value_comments = value_comments;
2540                            let redaction_ctx = de.peek_scalar_redaction_ctx()?;
2541                            let res =
2542                                with_subtree_redaction(redaction_ctx, || seed.deserialize(de))
2543                                    .map_err(|e| {
2544                                        attach_alias_locations_if_missing(
2545                                            e,
2546                                            alias_reference_location,
2547                                            defined_location,
2548                                        )
2549                                    });
2550                            recorder.current = prev;
2551                            return res;
2552                        }
2553                    }
2554
2555                    let mut de = YamlDeserializer::new(self.ev, self.cfg);
2556                    de.pending_comments = field_comments;
2557                    de.pending_value_separator_comments = value_separator_comments;
2558                    de.pending_value_comments = value_comments;
2559                    let redaction_ctx = de.peek_scalar_redaction_ctx()?;
2560                    with_scalar_redaction(redaction_ctx, || seed.deserialize(de)).map_err(|e| {
2561                        attach_alias_locations_if_missing(
2562                            e,
2563                            alias_reference_location,
2564                            defined_location,
2565                        )
2566                    })
2567                }
2568            }
2569        }
2570
2571        let (pending, pending_first_key_comments, live_done) =
2572            if self.struct_mode && matches!(self.cfg.dup_policy, DuplicateKeyPolicy::LastWins) {
2573                (
2574                    collect_last_wins_entries(
2575                        self.ev,
2576                        map_start_comments,
2577                        self.cfg,
2578                        None,
2579                        &HashSet::new(),
2580                        VecDeque::new(),
2581                    )?,
2582                    Vec::new(),
2583                    true,
2584                )
2585            } else {
2586                (VecDeque::new(), map_start_comments, false)
2587            };
2588
2589        #[cfg(any(feature = "garde", feature = "validator"))]
2590        let garde = self.garde;
2591
2592        let mut access = MA {
2593            ev: self.ev,
2594            cfg: child_cfg,
2595            struct_mode: self.struct_mode,
2596            have_key: false,
2597
2598            fallback_guard: None,
2599
2600            #[cfg(any(feature = "garde", feature = "validator"))]
2601            garde,
2602            #[cfg(any(feature = "garde", feature = "validator"))]
2603            pending_path_segment: None,
2604
2605            seen: if remembers_seen_keys(&child_cfg) {
2606                HashSet::with_capacity(8)
2607            } else {
2608                HashSet::new()
2609            },
2610            pending,
2611            merge_stack: VecDeque::new(),
2612            flushing_merges: false,
2613            live_done,
2614            pending_value: None,
2615            pending_field_comments: Vec::new(),
2616            pending_value_separator_comments: Vec::new(),
2617            pending_value_comments: Vec::new(),
2618            pending_first_key_comments,
2619        };
2620        let result = visitor.visit_map(&mut access)?;
2621        access.finish()?;
2622        Ok(result)
2623    }
2624
2625    /// **Delegates struct deserialization** to the same machinery as mappings.
2626    ///
2627    /// `Visitor` origin: From Serde for the caller’s
2628    /// Rust struct type (usually generated by `#[derive(Deserialize)]`). That
2629    /// visitor expects a `MapAccess` yielding field names/values.  
2630    /// **Where does it go?** We call `visitor.visit_map(..)` via `deserialize_map`,
2631    /// which streams YAML mapping pairs as struct fields.
2632    fn deserialize_struct<V: Visitor<'de>>(
2633        self,
2634        _name: &'static str,
2635        _fields: &'static [&'static str],
2636        visitor: V,
2637    ) -> Result<V::Value, Self::Error> {
2638        let mut de = self;
2639        de.struct_mode = true;
2640        de.deserialize_map(visitor)
2641    }
2642
2643    /// Deserialize an externally-tagged enum in either `Variant` or `{ Variant: value }` form.
2644    ///
2645    /// `Visitor` origin: From Serde for the target enum type.
2646    /// Flow: We surface an `EnumAccess` (`EA`) that provides the variant
2647    /// name, and a `VariantAccess` (`VA`) that reads the payload (unit/newtype/tuple/struct).
2648    fn deserialize_enum<V: Visitor<'de>>(
2649        mut self,
2650        name: &'static str,
2651        variants: &'static [&'static str],
2652        visitor: V,
2653    ) -> Result<V::Value, Self::Error> {
2654        enum Mode<'de, 'a> {
2655            Unit(EnumScalarId<'de>),
2656            Map(String, Location),
2657            /// Tag selects the variant, scalar value is the newtype payload.
2658            TaggedNewtype(EnumScalarId<'de>, RecordedEvents<'a>),
2659        }
2660
2661        let mut tagged_enum = None;
2662
2663        let peeked_ev = self.ev.peek()?.cloned();
2664        let mode = match peeked_ev {
2665            Some(Ev::RecursiveAlias { location, .. }) => {
2666                return Err(Error::RecursiveReferencesRequireWeakTypes { location });
2667            }
2668            Some(Ev::Scalar {
2669                tag,
2670                style,
2671                value: _,
2672                raw_tag,
2673                location,
2674                ..
2675            }) => {
2676                if let Some(tag_name) = simple_tagged_enum_name(&raw_tag, &tag) {
2677                    tagged_enum = Some((tag_name, location));
2678                }
2679                // A matching application tag selects the variant. Resolve and validate the
2680                // scalar as its payload, which may be null or a non-string in no_schema mode.
2681                if let Some((ref tag_name, tag_loc)) = tagged_enum
2682                    && variants.contains(&tag_name.as_str())
2683                {
2684                    let variant_name = EnumScalarId {
2685                        raw: Cow::Owned(tag_name.clone()),
2686                        effective: Cow::Owned(tag_name.clone()),
2687                        interpolated: false,
2688                        location: tag_loc,
2689                    };
2690                    // Consume the scalar and re-emit it without the tag for payload deserialization.
2691                    let origin = self.ev.node_origin();
2692                    let Some(mut ev) = self.ev.next()? else {
2693                        return Err(eof_with_loc(self.ev));
2694                    };
2695                    ev.strip_node_tag();
2696                    let replay = RecordedEvents::from_event(ev, origin);
2697                    tagged_enum = None; // prevent mismatch check
2698                    Mode::TaggedNewtype(variant_name, replay)
2699                } else {
2700                    let Some(view) = self.peek_scalar_view()? else {
2701                        return Err(eof_with_loc(self.ev));
2702                    };
2703                    self.validate_scalar_tag(
2704                        view.tag,
2705                        SfTag::String,
2706                        "enum variant",
2707                        view.location,
2708                    )?;
2709                    if self.cfg.no_schema
2710                        && !view.tag.forces_string()
2711                        && maybe_not_string(&view.effective, &style, self.cfg.strict_booleans)
2712                    {
2713                        let view = self.take_scalar_view()?;
2714                        return Err(Self::quoting_required_for_scalar(&view));
2715                    }
2716                    let view = self.take_scalar_view()?;
2717                    Mode::Unit(EnumScalarId::from_view(view))
2718                }
2719            }
2720            Some(Ev::MapStart {
2721                tag,
2722                raw_tag,
2723                location,
2724                ..
2725            }) => {
2726                // `!map` is classified as `SfTag::Map` for compatibility when used as
2727                // an ordinary mapping tag. In a typed enum context it may instead select
2728                // a renamed `map` variant. Resolved core forms (`!!map`, verbatim tags,
2729                // and directive-expanded handles) have a different `raw_tag` and remain
2730                // YAML type annotations.
2731                let tag_name = simple_tagged_enum_name(&raw_tag, &tag).or_else(|| {
2732                    (tag == SfTag::Map && raw_tag.as_deref() == Some("!map"))
2733                        .then(|| "map".to_owned())
2734                });
2735                if let Some(tag_name) = tag_name
2736                    && variants.contains(&tag_name.as_str())
2737                {
2738                    self.ev.strip_peeked_node_tag()?;
2739                    return visitor.visit_enum(EA {
2740                        ev: self.ev,
2741                        cfg: self.cfg,
2742                        variant: EnumScalarId {
2743                            raw: Cow::Owned(tag_name.clone()),
2744                            effective: Cow::Owned(tag_name),
2745                            interpolated: false,
2746                            location,
2747                        },
2748                        map_mode: false,
2749                        tagged_payload: true,
2750                    });
2751                }
2752
2753                self.expect_map_start()?;
2754                let mut key_de = YamlDeserializer::new(&mut *self.ev, self.cfg);
2755                key_de.in_key = true;
2756                if let Some(view) = key_de.peek_scalar_view()? {
2757                    key_de.validate_scalar_tag(
2758                        view.tag,
2759                        SfTag::String,
2760                        "enum variant",
2761                        view.location,
2762                    )?;
2763                    if self.cfg.no_schema
2764                        && !view.tag.forces_string()
2765                        && maybe_not_string(&view.raw, &view.style, self.cfg.strict_booleans)
2766                    {
2767                        let view = key_de.take_scalar_view()?;
2768                        return Err(Self::quoting_required_for_scalar(&view));
2769                    }
2770                    let view = key_de.take_scalar_view()?;
2771                    Mode::Map(view.raw.into_owned(), view.location)
2772                } else {
2773                    match self.ev.next()? {
2774                        Some(other) => {
2775                            return Err(Error::ExpectedStringKeyForExternallyTaggedEnum {
2776                                location: other.location(),
2777                            });
2778                        }
2779                        None => return Err(eof_with_loc(self.ev)),
2780                    }
2781                }
2782            }
2783            Some(Ev::SeqStart {
2784                tag,
2785                raw_tag,
2786                location,
2787                ..
2788            }) => {
2789                if let Some(tag_name) = simple_tagged_enum_name(&raw_tag, &tag)
2790                    && variants.contains(&tag_name.as_str())
2791                {
2792                    // Consume the SeqStart, collect all events until SeqEnd, replay as untagged sequence
2793                    let origin = self.ev.node_origin();
2794                    let Some(seq_start) = self.ev.next()? else {
2795                        return Err(eof_with_loc(self.ev));
2796                    };
2797                    let start_loc = seq_start.location();
2798                    let mut replay_events = RecordedEvents::new();
2799                    // Re-emit SeqStart without tag
2800                    if let Ev::SeqStart {
2801                        anchor, location, ..
2802                    } = seq_start
2803                    {
2804                        replay_events.push(
2805                            Ev::SeqStart {
2806                                anchor,
2807                                tag: SfTag::None,
2808                                raw_tag: None,
2809                                location,
2810                            },
2811                            origin,
2812                        );
2813                    }
2814                    let mut depth = 1usize;
2815                    while depth > 0 {
2816                        let _ = self.ev.peek()?;
2817                        let origin = self.ev.node_origin();
2818                        match self.ev.next()? {
2819                            Some(ev @ (Ev::SeqStart { .. } | Ev::MapStart { .. })) => {
2820                                depth += 1;
2821                                replay_events.push(ev, origin);
2822                            }
2823                            Some(ev @ (Ev::SeqEnd { .. } | Ev::MapEnd { .. })) => {
2824                                depth -= 1;
2825                                replay_events.push(ev, origin);
2826                            }
2827                            Some(ev) => {
2828                                replay_events.push(ev, origin);
2829                            }
2830                            None => return Err(eof_with_loc(self.ev)),
2831                        }
2832                    }
2833                    let replay = Box::new(ReplayEvents::new(
2834                        replay_events,
2835                        #[cfg(feature = "properties")]
2836                        self.ev.property_interpolation().clone(),
2837                    ));
2838                    return visitor.visit_enum(TaggedEA {
2839                        replay,
2840                        cfg: self.cfg,
2841                        variant: EnumScalarId {
2842                            raw: Cow::Owned(tag_name.clone()),
2843                            effective: Cow::Owned(tag_name),
2844                            interpolated: false,
2845                            location: start_loc,
2846                        },
2847                    });
2848                }
2849                return Err(Error::ExternallyTaggedEnumExpectedScalarOrMapping { location });
2850            }
2851            Some(Ev::SeqEnd { location }) => {
2852                return Err(Error::UnexpectedSequenceEnd { location });
2853            }
2854            Some(Ev::MapEnd { location }) => {
2855                return Err(Error::UnexpectedMappingEnd { location });
2856            }
2857            Some(Ev::Taken { location }) => {
2858                return Err(Error::unexpected("consumed event").with_location(location));
2859            }
2860            None => return Err(eof_with_loc(self.ev)),
2861        };
2862
2863        if let Some((tag_name, location)) = tagged_enum
2864            && tag_name != name
2865        {
2866            return Err(Error::TaggedEnumMismatch {
2867                tagged: tag_name,
2868                target: name,
2869                location,
2870            });
2871        }
2872
2873        struct EA<'de, 'e> {
2874            ev: &'e mut dyn Events<'de>,
2875            cfg: Cfg,
2876            variant: EnumScalarId<'de>,
2877            map_mode: bool,
2878            tagged_payload: bool,
2879        }
2880
2881        impl<'de, 'e> de::EnumAccess<'de> for EA<'de, 'e> {
2882            type Error = Error;
2883            type Variant = VA<'de, 'e>;
2884
2885            /// Provide the variant identifier to Serde and return a `VariantAccess`.
2886            fn variant_seed<Vv>(self, seed: Vv) -> Result<(Vv::Value, Self::Variant), Error>
2887            where
2888                Vv: de::DeserializeSeed<'de>,
2889            {
2890                let EA {
2891                    ev,
2892                    cfg,
2893                    variant,
2894                    map_mode,
2895                    tagged_payload,
2896                } = self;
2897                let v = seed.deserialize(variant.into_deserializer())?;
2898                Ok((
2899                    v,
2900                    VA {
2901                        ev,
2902                        cfg,
2903                        map_mode,
2904                        tagged_payload,
2905                    },
2906                ))
2907            }
2908        }
2909
2910        struct VA<'de, 'e> {
2911            ev: &'e mut dyn Events<'de>,
2912            cfg: Cfg,
2913            map_mode: bool,
2914            tagged_payload: bool,
2915        }
2916
2917        impl VA<'_, '_> {
2918            /// In map mode (`{ Variant: ... }`) ensure the closing `}` is present.
2919            fn expect_map_end(&mut self) -> Result<(), Error> {
2920                match self.ev.next()? {
2921                    Some(Ev::MapEnd { .. }) => Ok(()),
2922                    Some(other) => Err(Error::ExpectedMappingEndAfterEnumVariantValue {
2923                        location: other.location(),
2924                    }),
2925                    None => Err(eof_with_loc(self.ev)),
2926                }
2927            }
2928        }
2929
2930        impl<'de> de::VariantAccess<'de> for VA<'de, '_> {
2931            type Error = Error;
2932
2933            /// Handle unit variants: `Variant` or `{ Variant: null/~ }`.
2934            fn unit_variant(mut self) -> Result<(), Error> {
2935                if self.map_mode {
2936                    if YamlDeserializer::new(self.ev, self.cfg).peek_null_value()? {
2937                        let _ = self.ev.next()?; // consume the null-like scalar
2938                        return self.expect_map_end();
2939                    }
2940                    match self.ev.peek()? {
2941                        Some(Ev::MapEnd { .. }) => {
2942                            let _ = self.ev.next()?;
2943                            Ok(())
2944                        }
2945                        Some(other) => Err(Error::UnexpectedValueForUnitEnumVariant {
2946                            location: other.location(),
2947                        }),
2948                        None => Err(eof_with_loc(self.ev)),
2949                    }
2950                } else if self.tagged_payload {
2951                    de::Deserialize::deserialize(YamlDeserializer::new(self.ev, self.cfg))
2952                } else {
2953                    Ok(())
2954                }
2955            }
2956
2957            /// Handle newtype variants by delegating into `Deser`.
2958            fn newtype_variant_seed<T>(mut self, seed: T) -> Result<T::Value, Error>
2959            where
2960                T: de::DeserializeSeed<'de>,
2961            {
2962                // Get locations for error reporting before deserializing.
2963                let defined_location = match self.ev.peek()? {
2964                    Some(ev) => ev.location(),
2965                    None => self.ev.last_location(),
2966                };
2967                let alias_reference_location = self.ev.alias_reference_location();
2968
2969                let mut de = YamlDeserializer::new(self.ev, self.cfg);
2970                let redaction_ctx = de.peek_scalar_redaction_ctx()?;
2971                let value = with_subtree_redaction(redaction_ctx, || seed.deserialize(de))
2972                    .map_err(|e| {
2973                        attach_alias_locations_if_missing(
2974                            e,
2975                            alias_reference_location,
2976                            defined_location,
2977                        )
2978                    })?;
2979                if self.map_mode {
2980                    self.expect_map_end()?;
2981                }
2982                Ok(value)
2983            }
2984
2985            /// Handle tuple variants via `deserialize_tuple`.
2986            fn tuple_variant<Vv>(mut self, len: usize, visitor: Vv) -> Result<Vv::Value, Error>
2987            where
2988                Vv: Visitor<'de>,
2989            {
2990                let result =
2991                    YamlDeserializer::new(self.ev, self.cfg).deserialize_tuple(len, visitor)?;
2992                if self.map_mode {
2993                    self.expect_map_end()?;
2994                }
2995                Ok(result)
2996            }
2997
2998            /// Handle struct variants via `deserialize_struct`.
2999            fn struct_variant<Vv>(
3000                mut self,
3001                fields: &'static [&'static str],
3002                visitor: Vv,
3003            ) -> Result<Vv::Value, Error>
3004            where
3005                Vv: Visitor<'de>,
3006            {
3007                let result = YamlDeserializer::new(self.ev, self.cfg)
3008                    .deserialize_struct("", fields, visitor)?;
3009                if self.map_mode {
3010                    self.expect_map_end()?;
3011                }
3012                Ok(result)
3013            }
3014        }
3015
3016        struct TaggedEA<'de> {
3017            replay: Box<ReplayEvents<'de>>,
3018            cfg: Cfg,
3019            variant: EnumScalarId<'de>,
3020        }
3021
3022        impl<'de> de::EnumAccess<'de> for TaggedEA<'de> {
3023            type Error = Error;
3024            type Variant = TaggedVA<'de>;
3025
3026            fn variant_seed<Vv>(self, seed: Vv) -> Result<(Vv::Value, Self::Variant), Error>
3027            where
3028                Vv: de::DeserializeSeed<'de>,
3029            {
3030                let v = seed.deserialize(self.variant.into_deserializer())?;
3031                Ok((
3032                    v,
3033                    TaggedVA {
3034                        replay: self.replay,
3035                        cfg: self.cfg,
3036                    },
3037                ))
3038            }
3039        }
3040
3041        struct TaggedVA<'de> {
3042            replay: Box<ReplayEvents<'de>>,
3043            cfg: Cfg,
3044        }
3045
3046        impl<'de> de::VariantAccess<'de> for TaggedVA<'de> {
3047            type Error = Error;
3048
3049            fn unit_variant(mut self) -> Result<(), Error> {
3050                de::Deserialize::deserialize(YamlDeserializer::new(&mut *self.replay, self.cfg))
3051            }
3052
3053            fn newtype_variant_seed<T>(mut self, seed: T) -> Result<T::Value, Error>
3054            where
3055                T: de::DeserializeSeed<'de>,
3056            {
3057                let mut de = YamlDeserializer::new(&mut *self.replay, self.cfg);
3058                let redaction_ctx = de.peek_scalar_redaction_ctx()?;
3059                with_subtree_redaction(redaction_ctx, || seed.deserialize(de))
3060            }
3061
3062            fn tuple_variant<Vv>(mut self, len: usize, visitor: Vv) -> Result<Vv::Value, Error>
3063            where
3064                Vv: Visitor<'de>,
3065            {
3066                YamlDeserializer::new(&mut *self.replay, self.cfg).deserialize_tuple(len, visitor)
3067            }
3068
3069            fn struct_variant<Vv>(
3070                mut self,
3071                fields: &'static [&'static str],
3072                visitor: Vv,
3073            ) -> Result<Vv::Value, Error>
3074            where
3075                Vv: Visitor<'de>,
3076            {
3077                YamlDeserializer::new(&mut *self.replay, self.cfg)
3078                    .deserialize_struct("", fields, visitor)
3079            }
3080        }
3081
3082        let access = match mode {
3083            Mode::Unit(variant) => EA {
3084                ev: self.ev,
3085                cfg: self.cfg,
3086                variant,
3087                map_mode: false,
3088                tagged_payload: false,
3089            },
3090            Mode::Map(variant, variant_location) => EA {
3091                ev: self.ev,
3092                cfg: self.cfg,
3093                variant: EnumScalarId {
3094                    raw: Cow::Owned(variant.clone()),
3095                    effective: Cow::Owned(variant),
3096                    interpolated: false,
3097                    location: variant_location,
3098                },
3099                map_mode: true,
3100                tagged_payload: false,
3101            },
3102            Mode::TaggedNewtype(variant, replay_buf) => {
3103                let replay = Box::new(ReplayEvents::new(
3104                    replay_buf,
3105                    #[cfg(feature = "properties")]
3106                    self.ev.property_interpolation().clone(),
3107                ));
3108                // We need to use a replay source for the payload
3109                return visitor.visit_enum(TaggedEA {
3110                    replay,
3111                    cfg: self.cfg,
3112                    variant,
3113                });
3114            }
3115        };
3116
3117        visitor.visit_enum(access)
3118    }
3119
3120    /// Deserialize an identifier (e.g., struct field name); treated as string.
3121    fn deserialize_identifier<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
3122        self.deserialize_str(visitor)
3123    }
3124
3125    /// Deserialize a value that the caller intends to ignore.
3126    ///
3127    /// Use the same tag validation and conversions as other untyped reads.
3128    fn deserialize_ignored_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
3129        self.deserialize_any(visitor)
3130    }
3131}
3132
3133#[cfg(all(test, feature = "properties"))]
3134mod tests {
3135    use super::super::events::PropertyInterpolation;
3136    use super::super::options::{Options, PropertySyntax};
3137    use super::*;
3138
3139    #[test]
3140    fn effective_scalar_value_without_property_map_returns_original_scalar() {
3141        let budget = crate::Budget::default();
3142        let mut events = ReplayEvents::new(
3143            Vec::new(),
3144            PropertyInterpolation::new(None, PropertySyntax::Braced, Some(&budget)),
3145        );
3146        let de = YamlDeserializer::new(&mut events, Cfg::from_options(&Options::default()));
3147
3148        let value = de
3149            .effective_scalar_value(Cow::Borrowed("${MISSING}"), Location::new(1, 1))
3150            .expect("missing property map should not be treated as interpolation");
3151
3152        assert_eq!(value, Cow::Borrowed("${MISSING}"));
3153    }
3154}