Skip to main content

noyalib/value/
serde_impl.rs

1//! serde `Serialize`/`Deserialize` for `Value`.
2
3// SPDX-License-Identifier: MIT OR Apache-2.0
4// Copyright (c) 2026 Noyalib. All rights reserved.
5
6use super::{Mapping, Number, Tag, TaggedValue, Value};
7use crate::prelude::*;
8use indexmap::map::Iter;
9
10impl<'de> serde_core::Deserialize<'de> for Value {
11    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
12    where
13        D: serde_core::Deserializer<'de>,
14    {
15        struct ValueVisitor;
16
17        impl<'de> serde_core::de::Visitor<'de> for ValueVisitor {
18            type Value = Value;
19
20            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
21                formatter.write_str("any valid YAML value")
22            }
23
24            fn visit_bool<E>(self, v: bool) -> Result<Value, E> {
25                Ok(Value::Bool(v))
26            }
27
28            fn visit_i64<E>(self, v: i64) -> Result<Value, E> {
29                Ok(Value::Number(Number::Integer(v)))
30            }
31
32            fn visit_u64<E>(self, v: u64) -> Result<Value, E> {
33                Ok(Value::Number(Number::from(v)))
34            }
35
36            fn visit_f64<E>(self, v: f64) -> Result<Value, E> {
37                Ok(Value::Number(Number::Float(v)))
38            }
39
40            fn visit_str<E>(self, v: &str) -> Result<Value, E> {
41                Ok(Value::String(v.to_owned()))
42            }
43
44            fn visit_string<E>(self, v: String) -> Result<Value, E> {
45                Ok(Value::String(v))
46            }
47
48            fn visit_none<E>(self) -> Result<Value, E> {
49                Ok(Value::Null)
50            }
51
52            fn visit_unit<E>(self) -> Result<Value, E> {
53                Ok(Value::Null)
54            }
55
56            fn visit_enum<A>(self, data: A) -> Result<Value, A::Error>
57            where
58                A: serde_core::de::EnumAccess<'de>,
59            {
60                // The AST `Deserializer`'s `deserialize_any` routes a
61                // tagged node here (variant name = the YAML tag, payload
62                // = the untagged inner value) instead of transparently
63                // descending into it, so a `Value` reached through serde
64                // — nested inside a `Mapping`, a `Sequence`/`Vec<Value>`,
65                // or a struct field of type `Value` — keeps its tag the
66                // same way the top-level `Value` target already does.
67                // See #350.
68                use serde_core::de::VariantAccess as _;
69                let (tag, variant): (String, A::Variant) = data.variant()?;
70                let value: Value = variant.newtype_variant()?;
71                Ok(Value::Tagged(Box::new(TaggedValue::new(
72                    Tag::new(tag),
73                    value,
74                ))))
75            }
76
77            fn visit_seq<A>(self, mut seq: A) -> Result<Value, A::Error>
78            where
79                A: serde_core::de::SeqAccess<'de>,
80            {
81                // Pre-size from the SeqAccess size_hint when
82                // available — saves up to ~11 reallocations on a
83                // 2 000-element sequence (Vec doubles on each
84                // grow). Falls back to the default growth strategy
85                // when the hint isn't reliable.
86                let mut vec = match seq.size_hint() {
87                    Some(n) if n > 0 && n < 1 << 20 => Vec::with_capacity(n),
88                    _ => Vec::new(),
89                };
90                while let Some(elem) = seq.next_element()? {
91                    vec.push(elem);
92                }
93                Ok(Value::Sequence(vec))
94            }
95
96            fn visit_map<A>(self, mut map: A) -> Result<Value, A::Error>
97            where
98                A: serde_core::de::MapAccess<'de>,
99            {
100                let first_key: Option<String> = map.next_key()?;
101                // Regular mapping path — collect every (k, v) pair
102                // including the (k, v) we already consumed.
103                // Pre-size when the MapAccess provides a usable
104                // hint; saves ~10 IndexMap rehashes on large
105                // mappings (capacity grows by ~doubling).
106                let mut mapping = match map.size_hint() {
107                    Some(n) if n > 0 && n < 1 << 20 => Mapping::with_capacity(n),
108                    _ => Mapping::new(),
109                };
110                if let Some(k) = first_key {
111                    let v: Value = map.next_value()?;
112                    let _ = mapping.insert(k, v);
113                }
114                while let Some((key, value)) = map.next_entry::<String, Value>()? {
115                    let _ = mapping.insert(key, value);
116                }
117                Ok(Value::Mapping(mapping))
118            }
119        }
120
121        deserializer.deserialize_any(ValueVisitor)
122    }
123}
124
125impl serde_core::Serialize for Value {
126    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
127    where
128        S: serde_core::Serializer,
129    {
130        match self {
131            Self::Null => serializer.serialize_none(),
132            Self::Bool(b) => serializer.serialize_bool(*b),
133            Self::Number(Number::Integer(n)) => serializer.serialize_i64(*n),
134            #[cfg(feature = "lossless-u64")]
135            Self::Number(Number::Unsigned(n)) => serializer.serialize_u64(*n),
136            Self::Number(Number::Float(n)) => serializer.serialize_f64(*n),
137            Self::String(s) => serializer.serialize_str(s),
138            Self::Sequence(s) => s.serialize(serializer),
139            Self::Mapping(m) => {
140                use serde_core::ser::SerializeMap as _;
141                let mut map = serializer.serialize_map(Some(m.len()))?;
142                for (k, v) in m {
143                    map.serialize_entry(k, v)?;
144                }
145                map.end()
146            }
147            // `TaggedValue::serialize` owns the wire form (a marker newtype
148            // around a single-entry map keyed by the tag string); one
149            // definition keeps this arm and the standalone impl in step.
150            Self::Tagged(tagged) => serde_core::Serialize::serialize(tagged.as_ref(), serializer),
151        }
152    }
153}
154
155// ============================================================================
156// Deserializer implementation for &Value
157// ============================================================================
158
159impl<'de> serde_core::de::IntoDeserializer<'de, crate::Error> for &'de Value {
160    type Deserializer = Self;
161
162    fn into_deserializer(self) -> Self::Deserializer {
163        self
164    }
165}
166
167struct ValueSeqAccess<'de> {
168    iter: core::slice::Iter<'de, Value>,
169}
170
171impl<'de> serde_core::de::SeqAccess<'de> for ValueSeqAccess<'de> {
172    type Error = crate::Error;
173
174    fn next_element_seed<T>(&mut self, seed: T) -> crate::Result<Option<T::Value>>
175    where
176        T: serde_core::de::DeserializeSeed<'de>,
177    {
178        match self.iter.next() {
179            Some(value) => seed.deserialize(value).map(Some),
180            None => Ok(None),
181        }
182    }
183}
184
185struct ValueMapAccess<'de> {
186    iter: Iter<'de, String, Value>,
187    value: Option<&'de Value>,
188}
189
190impl<'de> serde_core::de::MapAccess<'de> for ValueMapAccess<'de> {
191    type Error = crate::Error;
192
193    fn next_key_seed<K>(&mut self, seed: K) -> crate::Result<Option<K::Value>>
194    where
195        K: serde_core::de::DeserializeSeed<'de>,
196    {
197        match self.iter.next() {
198            Some((key, value)) => {
199                self.value = Some(value);
200                seed.deserialize(serde_core::de::value::BorrowedStrDeserializer::new(key))
201                    .map(Some)
202            }
203            None => Ok(None),
204        }
205    }
206
207    fn next_value_seed<V>(&mut self, seed: V) -> crate::Result<V::Value>
208    where
209        V: serde_core::de::DeserializeSeed<'de>,
210    {
211        match self.value.take() {
212            Some(value) => seed.deserialize(value),
213            None => Err(serde_core::de::Error::custom("value is missing")),
214        }
215    }
216}
217
218impl<'de> serde_core::Deserializer<'de> for &'de Value {
219    type Error = crate::Error;
220
221    fn deserialize_any<V>(self, visitor: V) -> crate::Result<V::Value>
222    where
223        V: serde_core::de::Visitor<'de>,
224    {
225        match self {
226            Value::Null => visitor.visit_unit(),
227            Value::Bool(b) => visitor.visit_bool(*b),
228            Value::Number(Number::Integer(n)) => visitor.visit_i64(*n),
229            #[cfg(feature = "lossless-u64")]
230            Value::Number(Number::Unsigned(n)) => visitor.visit_u64(*n),
231            Value::Number(Number::Float(n)) => visitor.visit_f64(*n),
232            Value::String(s) => visitor.visit_borrowed_str(s),
233            Value::Sequence(seq) => visitor.visit_seq(ValueSeqAccess { iter: seq.iter() }),
234            Value::Mapping(map) => visitor.visit_map(ValueMapAccess {
235                iter: map.iter(),
236                value: None,
237            }),
238            Value::Tagged(tagged) => {
239                let tagged_ref: &'de TaggedValue = tagged;
240                serde_core::Deserializer::deserialize_any(tagged_ref, visitor)
241            }
242        }
243    }
244
245    fn deserialize_enum<V>(
246        self,
247        name: &'static str,
248        variants: &'static [&'static str],
249        visitor: V,
250    ) -> crate::Result<V::Value>
251    where
252        V: serde_core::de::Visitor<'de>,
253    {
254        match self {
255            Value::Tagged(tagged) => {
256                let tagged_ref: &'de TaggedValue = tagged;
257                serde_core::Deserializer::deserialize_enum(tagged_ref, name, variants, visitor)
258            }
259            Value::String(s) => visitor
260                .visit_enum(serde_core::de::value::BorrowedStrDeserializer::<crate::Error>::new(s)),
261            _ => serde_core::Deserializer::deserialize_any(self, visitor),
262        }
263    }
264
265    fn deserialize_seq<V>(self, visitor: V) -> crate::Result<V::Value>
266    where
267        V: serde_core::de::Visitor<'de>,
268    {
269        match self {
270            Value::Sequence(seq) => visitor.visit_seq(ValueSeqAccess { iter: seq.iter() }),
271            _ => serde_core::Deserializer::deserialize_any(self, visitor),
272        }
273    }
274
275    fn deserialize_map<V>(self, visitor: V) -> crate::Result<V::Value>
276    where
277        V: serde_core::de::Visitor<'de>,
278    {
279        match self {
280            Value::Mapping(map) => visitor.visit_map(ValueMapAccess {
281                iter: map.iter(),
282                value: None,
283            }),
284            _ => serde_core::Deserializer::deserialize_any(self, visitor),
285        }
286    }
287
288    fn deserialize_struct<V>(
289        self,
290        name: &'static str,
291        _fields: &'static [&'static str],
292        visitor: V,
293    ) -> crate::Result<V::Value>
294    where
295        V: serde_core::de::Visitor<'de>,
296    {
297        if name == crate::spanned::SPANNED_TYPE_NAME {
298            return visitor.visit_map(crate::de::SpannedMapAccess::new(self, None));
299        }
300        serde_core::Deserializer::deserialize_map(self, visitor)
301    }
302
303    fn deserialize_option<V>(self, visitor: V) -> crate::Result<V::Value>
304    where
305        V: serde_core::de::Visitor<'de>,
306    {
307        // `option` used to be in the `forward_to_deserialize_any!` list
308        // below, which sent every value straight to `deserialize_any`.
309        // For anything other than null that hands the visitor a concrete
310        // scalar — `visit_borrowed_str`, `visit_i64` — and serde's
311        // `Option` visitor rejects it:
312        //
313        //     invalid type: string "hello", expected option
314        //
315        // Only `~` happened to work, because `deserialize_any` maps
316        // `Value::Null` to `visit_unit` and the `Option` visitor accepts
317        // that as `None`. Every *populated* Option field failed, for any
318        // `T`. See #239, which reported it for `Option<String>`; it was
319        // never specific to strings or to empty ones.
320        match self {
321            Value::Null => visitor.visit_none(),
322            _ => visitor.visit_some(self),
323        }
324    }
325
326    serde_core::forward_to_deserialize_any! {
327        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes
328        byte_buf unit unit_struct newtype_struct tuple
329        tuple_struct identifier ignored_any
330    }
331}