Skip to main content

hocon/
serde.rs

1use crate::numeric_array::numeric_object_to_array;
2use crate::value::{HoconValue, ScalarType, ScalarValue};
3use indexmap::IndexMap;
4use std::fmt;
5
6// ---------------------------------------------------------------------------
7// Error type
8// ---------------------------------------------------------------------------
9
10#[non_exhaustive]
11#[derive(Debug)]
12pub struct DeserializeError {
13    pub message: String,
14}
15
16impl fmt::Display for DeserializeError {
17    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18        write!(f, "HOCON deserialization error: {}", self.message)
19    }
20}
21
22impl std::error::Error for DeserializeError {}
23
24impl ::serde::de::Error for DeserializeError {
25    fn custom<T: fmt::Display>(msg: T) -> Self {
26        DeserializeError {
27            message: msg.to_string(),
28        }
29    }
30}
31
32// ---------------------------------------------------------------------------
33// Deserializer
34// ---------------------------------------------------------------------------
35
36/// A `serde::Deserializer` over a borrowed [`HoconValue`].
37///
38/// Public so it can be handed to serde composition points (`DeserializeSeed`,
39/// `#[serde(deserialize_with = ...)]`, or generic code abstracting over a
40/// `Deserializer`). For the common "value → type" case prefer [`from_value`].
41pub struct HoconDeserializer<'de> {
42    value: &'de HoconValue,
43}
44
45impl<'de> HoconDeserializer<'de> {
46    /// Create a deserializer over the given borrowed value.
47    pub fn new(value: &'de HoconValue) -> Self {
48        Self { value }
49    }
50}
51
52/// Deserialize an arbitrary [`HoconValue`] fragment into a typed value.
53///
54/// Accepts **any** node — object, array, or scalar — with the same HOCON-aware
55/// coercion used by [`Config::deserialize`](crate::Config::deserialize). Use this
56/// when you hold a `&HoconValue` (e.g. from [`Config::get`](crate::Config::get) or
57/// a [`Config::get_list`](crate::Config::get_list) element) and want it as `T`.
58///
59/// Mirrors `serde_json::from_value`, but borrows the value rather than consuming it.
60pub fn from_value<T>(value: &HoconValue) -> Result<T, DeserializeError>
61where
62    T: ::serde::de::DeserializeOwned,
63{
64    T::deserialize(HoconDeserializer::new(value))
65}
66
67/// Helper: parse raw string as integer type with float truncation fallback.
68fn parse_int_from_scalar<T>(sv: &ScalarValue, type_name: &str) -> Result<T, DeserializeError>
69where
70    T: std::str::FromStr + TryFrom<i64>,
71    <T as std::str::FromStr>::Err: fmt::Display,
72    <T as TryFrom<i64>>::Error: fmt::Display,
73{
74    // Try direct parse
75    if let Ok(n) = sv.raw.parse::<T>() {
76        return Ok(n);
77    }
78    // Whole-number float/exponent coercion fallback, for any float-like raw
79    // (quoted or not) — consistent with `Config::get_i64`. Wholeness/value are
80    // derived from the raw text, never via f64 (xx.hocon#56).
81    if let Some(as_i64) = crate::value::whole_float_to_i64(&sv.raw) {
82        if let Ok(n) = T::try_from(as_i64) {
83            return Ok(n);
84        }
85    }
86    Err(DeserializeError {
87        message: format!("cannot parse \"{}\" as {}", sv.raw, type_name),
88    })
89}
90
91macro_rules! deserialize_int {
92    ($method:ident, $visit:ident, $ty:ty) => {
93        fn $method<V: ::serde::de::Visitor<'de>>(
94            self,
95            visitor: V,
96        ) -> Result<V::Value, Self::Error> {
97            match self.value {
98                HoconValue::Scalar(sv) => {
99                    let n: $ty = parse_int_from_scalar(sv, stringify!($ty))?;
100                    visitor.$visit(n)
101                }
102                other => Err(DeserializeError {
103                    message: format!("expected {}, got {:?}", stringify!($ty), other),
104                }),
105            }
106        }
107    };
108}
109
110impl<'de> ::serde::Deserializer<'de> for HoconDeserializer<'de> {
111    type Error = DeserializeError;
112
113    fn deserialize_any<V: ::serde::de::Visitor<'de>>(
114        self,
115        visitor: V,
116    ) -> Result<V::Value, Self::Error> {
117        match self.value {
118            HoconValue::Scalar(sv) => match sv.value_type {
119                ScalarType::Null => visitor.visit_unit(),
120                ScalarType::Boolean => visitor.visit_bool(sv.raw == "true"),
121                ScalarType::Number => {
122                    // Try i64 first (no dot/exponent), then f64
123                    if !sv.raw.contains('.') && !sv.raw.contains('e') && !sv.raw.contains('E') {
124                        if let Ok(n) = sv.raw.parse::<i64>() {
125                            return visitor.visit_i64(n);
126                        }
127                    }
128                    if let Ok(f) = sv.raw.parse::<f64>() {
129                        return visitor.visit_f64(f);
130                    }
131                    visitor.visit_string(sv.raw.clone())
132                }
133                ScalarType::String => visitor.visit_string(sv.raw.clone()),
134            },
135            HoconValue::Object(map) => visitor.visit_map(HoconMapAccess::new(map)),
136            HoconValue::Array(items) => visitor.visit_seq(HoconSeqAccess::new(items)),
137            HoconValue::Placeholder(pv) => Err(DeserializeError {
138                message: format!(
139                    "cannot deserialize unresolved substitution at path {:?}: call Config::resolve() first",
140                    pv.path
141                ),
142            }),
143        }
144    }
145
146    fn deserialize_bool<V: ::serde::de::Visitor<'de>>(
147        self,
148        visitor: V,
149    ) -> Result<V::Value, Self::Error> {
150        match self.value {
151            HoconValue::Scalar(sv) => match sv.raw.to_lowercase().as_str() {
152                "true" | "yes" | "on" => visitor.visit_bool(true),
153                "false" | "no" | "off" => visitor.visit_bool(false),
154                _ => Err(DeserializeError {
155                    message: format!("cannot coerce \"{}\" to bool", sv.raw),
156                }),
157            },
158            other => Err(DeserializeError {
159                message: format!("expected bool, got {:?}", other),
160            }),
161        }
162    }
163
164    deserialize_int!(deserialize_i8, visit_i8, i8);
165    deserialize_int!(deserialize_i16, visit_i16, i16);
166    deserialize_int!(deserialize_i32, visit_i32, i32);
167    deserialize_int!(deserialize_i64, visit_i64, i64);
168    deserialize_int!(deserialize_u8, visit_u8, u8);
169    deserialize_int!(deserialize_u16, visit_u16, u16);
170    deserialize_int!(deserialize_u32, visit_u32, u32);
171    deserialize_int!(deserialize_u64, visit_u64, u64);
172
173    fn deserialize_f32<V: ::serde::de::Visitor<'de>>(
174        self,
175        visitor: V,
176    ) -> Result<V::Value, Self::Error> {
177        match self.value {
178            HoconValue::Scalar(sv) => {
179                let f: f32 = sv.raw.parse().map_err(|_| DeserializeError {
180                    message: format!("cannot parse \"{}\" as f32", sv.raw),
181                })?;
182                visitor.visit_f32(f)
183            }
184            other => Err(DeserializeError {
185                message: format!("expected f32, got {:?}", other),
186            }),
187        }
188    }
189
190    fn deserialize_f64<V: ::serde::de::Visitor<'de>>(
191        self,
192        visitor: V,
193    ) -> Result<V::Value, Self::Error> {
194        match self.value {
195            HoconValue::Scalar(sv) => {
196                let f: f64 = sv.raw.parse().map_err(|_| DeserializeError {
197                    message: format!("cannot parse \"{}\" as f64", sv.raw),
198                })?;
199                visitor.visit_f64(f)
200            }
201            other => Err(DeserializeError {
202                message: format!("expected f64, got {:?}", other),
203            }),
204        }
205    }
206
207    fn deserialize_char<V: ::serde::de::Visitor<'de>>(
208        self,
209        visitor: V,
210    ) -> Result<V::Value, Self::Error> {
211        match self.value {
212            HoconValue::Scalar(sv) => {
213                let mut chars = sv.raw.chars();
214                match (chars.next(), chars.next()) {
215                    (Some(c), None) => visitor.visit_char(c),
216                    _ => Err(DeserializeError {
217                        message: format!("expected single char, got \"{}\"", sv.raw),
218                    }),
219                }
220            }
221            other => Err(DeserializeError {
222                message: format!("expected char, got {:?}", other),
223            }),
224        }
225    }
226
227    fn deserialize_str<V: ::serde::de::Visitor<'de>>(
228        self,
229        visitor: V,
230    ) -> Result<V::Value, Self::Error> {
231        self.deserialize_string(visitor)
232    }
233
234    fn deserialize_string<V: ::serde::de::Visitor<'de>>(
235        self,
236        visitor: V,
237    ) -> Result<V::Value, Self::Error> {
238        match self.value {
239            HoconValue::Scalar(sv) => visitor.visit_string(sv.raw.clone()),
240            other => Err(DeserializeError {
241                message: format!("expected string, got {:?}", other),
242            }),
243        }
244    }
245
246    fn deserialize_bytes<V: ::serde::de::Visitor<'de>>(
247        self,
248        _visitor: V,
249    ) -> Result<V::Value, Self::Error> {
250        Err(DeserializeError {
251            message: "HOCON does not support byte arrays".into(),
252        })
253    }
254
255    fn deserialize_byte_buf<V: ::serde::de::Visitor<'de>>(
256        self,
257        _visitor: V,
258    ) -> Result<V::Value, Self::Error> {
259        Err(DeserializeError {
260            message: "HOCON does not support byte arrays".into(),
261        })
262    }
263
264    fn deserialize_option<V: ::serde::de::Visitor<'de>>(
265        self,
266        visitor: V,
267    ) -> Result<V::Value, Self::Error> {
268        match self.value {
269            HoconValue::Scalar(sv) if sv.value_type == ScalarType::Null => visitor.visit_none(),
270            _ => visitor.visit_some(self),
271        }
272    }
273
274    fn deserialize_unit<V: ::serde::de::Visitor<'de>>(
275        self,
276        visitor: V,
277    ) -> Result<V::Value, Self::Error> {
278        match self.value {
279            HoconValue::Scalar(sv) if sv.value_type == ScalarType::Null => visitor.visit_unit(),
280            other => Err(DeserializeError {
281                message: format!("expected null/unit, got {:?}", other),
282            }),
283        }
284    }
285
286    fn deserialize_unit_struct<V: ::serde::de::Visitor<'de>>(
287        self,
288        _name: &'static str,
289        visitor: V,
290    ) -> Result<V::Value, Self::Error> {
291        self.deserialize_unit(visitor)
292    }
293
294    fn deserialize_newtype_struct<V: ::serde::de::Visitor<'de>>(
295        self,
296        _name: &'static str,
297        visitor: V,
298    ) -> Result<V::Value, Self::Error> {
299        visitor.visit_newtype_struct(self)
300    }
301
302    fn deserialize_seq<V: ::serde::de::Visitor<'de>>(
303        self,
304        visitor: V,
305    ) -> Result<V::Value, Self::Error> {
306        match self.value {
307            HoconValue::Array(items) => visitor.visit_seq(HoconSeqAccess::new(items)),
308            // S15 §"Accessor behaviour" L160-161: every typed-array accessor must invoke
309            // numeric_object_to_array. If the object has at least one eligible integer key,
310            // convert and deserialize as a sequence; otherwise fall through to the type-mismatch
311            // error so serde receives a meaningful message.
312            obj @ HoconValue::Object(_) => match numeric_object_to_array(obj) {
313                Some(items) => visitor.visit_seq(HoconOwnedSeqAccess::new(items)),
314                None => Err(DeserializeError {
315                    message: format!("expected array, got {:?}", obj),
316                }),
317            },
318            other => Err(DeserializeError {
319                message: format!("expected array, got {:?}", other),
320            }),
321        }
322    }
323
324    fn deserialize_tuple<V: ::serde::de::Visitor<'de>>(
325        self,
326        _len: usize,
327        visitor: V,
328    ) -> Result<V::Value, Self::Error> {
329        self.deserialize_seq(visitor)
330    }
331
332    fn deserialize_tuple_struct<V: ::serde::de::Visitor<'de>>(
333        self,
334        _name: &'static str,
335        _len: usize,
336        visitor: V,
337    ) -> Result<V::Value, Self::Error> {
338        self.deserialize_seq(visitor)
339    }
340
341    fn deserialize_map<V: ::serde::de::Visitor<'de>>(
342        self,
343        visitor: V,
344    ) -> Result<V::Value, Self::Error> {
345        match self.value {
346            HoconValue::Object(map) => visitor.visit_map(HoconMapAccess::new(map)),
347            other => Err(DeserializeError {
348                message: format!("expected object, got {:?}", other),
349            }),
350        }
351    }
352
353    fn deserialize_struct<V: ::serde::de::Visitor<'de>>(
354        self,
355        _name: &'static str,
356        _fields: &'static [&'static str],
357        visitor: V,
358    ) -> Result<V::Value, Self::Error> {
359        self.deserialize_map(visitor)
360    }
361
362    fn deserialize_enum<V: ::serde::de::Visitor<'de>>(
363        self,
364        _name: &'static str,
365        _variants: &'static [&'static str],
366        visitor: V,
367    ) -> Result<V::Value, Self::Error> {
368        match self.value {
369            HoconValue::Scalar(sv) => visitor.visit_enum(sv.raw.as_str().into_deserializer()),
370            other => Err(DeserializeError {
371                message: format!("expected string for enum variant, got {:?}", other),
372            }),
373        }
374    }
375
376    fn deserialize_identifier<V: ::serde::de::Visitor<'de>>(
377        self,
378        visitor: V,
379    ) -> Result<V::Value, Self::Error> {
380        self.deserialize_string(visitor)
381    }
382
383    fn deserialize_ignored_any<V: ::serde::de::Visitor<'de>>(
384        self,
385        visitor: V,
386    ) -> Result<V::Value, Self::Error> {
387        visitor.visit_unit()
388    }
389}
390
391use ::serde::de::IntoDeserializer;
392
393// ---------------------------------------------------------------------------
394// MapAccess
395// ---------------------------------------------------------------------------
396
397pub(crate) struct HoconMapAccess<'de> {
398    iter: indexmap::map::Iter<'de, String, HoconValue>,
399    current_value: Option<&'de HoconValue>,
400}
401
402impl<'de> HoconMapAccess<'de> {
403    fn new(map: &'de IndexMap<String, HoconValue>) -> Self {
404        Self {
405            iter: map.iter(),
406            current_value: None,
407        }
408    }
409}
410
411impl<'de> ::serde::de::MapAccess<'de> for HoconMapAccess<'de> {
412    type Error = DeserializeError;
413
414    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
415    where
416        K: ::serde::de::DeserializeSeed<'de>,
417    {
418        match self.iter.next() {
419            Some((key, value)) => {
420                self.current_value = Some(value);
421                seed.deserialize(StringKeyDeserializer { key }).map(Some)
422            }
423            None => Ok(None),
424        }
425    }
426
427    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
428    where
429        V: ::serde::de::DeserializeSeed<'de>,
430    {
431        let value = self.current_value.take().ok_or_else(|| DeserializeError {
432            message: "next_value_seed called before next_key_seed".into(),
433        })?;
434        seed.deserialize(HoconDeserializer::new(value))
435    }
436}
437
438// A simple key deserializer that yields a string.
439struct StringKeyDeserializer<'a> {
440    key: &'a str,
441}
442
443impl<'de, 'a> ::serde::Deserializer<'de> for StringKeyDeserializer<'a> {
444    type Error = DeserializeError;
445
446    fn deserialize_any<V: ::serde::de::Visitor<'de>>(
447        self,
448        visitor: V,
449    ) -> Result<V::Value, Self::Error> {
450        visitor.visit_str(self.key)
451    }
452
453    ::serde::forward_to_deserialize_any! {
454        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string
455        bytes byte_buf option unit unit_struct newtype_struct seq tuple
456        tuple_struct map struct enum identifier ignored_any
457    }
458}
459
460// ---------------------------------------------------------------------------
461// SeqAccess
462// ---------------------------------------------------------------------------
463
464pub(crate) struct HoconSeqAccess<'de> {
465    iter: std::slice::Iter<'de, HoconValue>,
466}
467
468impl<'de> HoconSeqAccess<'de> {
469    fn new(items: &'de [HoconValue]) -> Self {
470        Self { iter: items.iter() }
471    }
472}
473
474impl<'de> ::serde::de::SeqAccess<'de> for HoconSeqAccess<'de> {
475    type Error = DeserializeError;
476
477    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
478    where
479        T: ::serde::de::DeserializeSeed<'de>,
480    {
481        match self.iter.next() {
482            Some(value) => seed.deserialize(HoconDeserializer::new(value)).map(Some),
483            None => Ok(None),
484        }
485    }
486}
487
488// ---------------------------------------------------------------------------
489// OwnedSeqAccess — for sequences built from owned Vec<HoconValue>
490// (used when numeric_object_to_array produces a fresh Vec)
491// ---------------------------------------------------------------------------
492
493pub(crate) struct HoconOwnedSeqAccess {
494    /// Items in their original (forward) order.
495    items: Vec<HoconValue>,
496    /// Current index into items; advances on each `next_element_seed` call.
497    idx: usize,
498}
499
500impl HoconOwnedSeqAccess {
501    fn new(items: Vec<HoconValue>) -> Self {
502        Self { items, idx: 0 }
503    }
504}
505
506impl<'de> ::serde::de::SeqAccess<'de> for HoconOwnedSeqAccess {
507    type Error = DeserializeError;
508
509    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
510    where
511        T: ::serde::de::DeserializeSeed<'de>,
512    {
513        if self.idx >= self.items.len() {
514            return Ok(None);
515        }
516        // Clone the value at idx so OwnedHoconDeserializer can own it.
517        // The clone is bounded by the element count (typically small: O(n) where n
518        // is the number of numeric keys in the source object).
519        let value = self.items[self.idx].clone();
520        self.idx += 1;
521        seed.deserialize(OwnedHoconDeserializer { value }).map(Some)
522    }
523}
524
525/// A variant of HoconDeserializer that owns its value (no lifetime parameter).
526/// Used exclusively by HoconOwnedSeqAccess to avoid the lifetime issue when
527/// deserializing from an owned Vec<HoconValue>.
528///
529/// This duplicates some deserialize methods from HoconDeserializer; the
530/// duplication is justified by the need to avoid self-referential borrows.
531struct OwnedHoconDeserializer {
532    value: HoconValue,
533}
534
535impl<'de> ::serde::Deserializer<'de> for OwnedHoconDeserializer {
536    type Error = DeserializeError;
537
538    fn deserialize_any<V: ::serde::de::Visitor<'de>>(
539        self,
540        visitor: V,
541    ) -> Result<V::Value, Self::Error> {
542        match self.value {
543            HoconValue::Scalar(sv) => match sv.value_type {
544                ScalarType::Null => visitor.visit_unit(),
545                ScalarType::Boolean => visitor.visit_bool(sv.raw == "true"),
546                ScalarType::Number => {
547                    if !sv.raw.contains('.') && !sv.raw.contains('e') && !sv.raw.contains('E') {
548                        if let Ok(n) = sv.raw.parse::<i64>() {
549                            return visitor.visit_i64(n);
550                        }
551                    }
552                    if let Ok(f) = sv.raw.parse::<f64>() {
553                        return visitor.visit_f64(f);
554                    }
555                    visitor.visit_string(sv.raw)
556                }
557                ScalarType::String => visitor.visit_string(sv.raw),
558            },
559            HoconValue::Object(map) => visitor.visit_map(OwnedHoconMapAccess::new(map)),
560            HoconValue::Array(items) => visitor.visit_seq(HoconOwnedSeqAccess::new(items)),
561            HoconValue::Placeholder(pv) => Err(DeserializeError {
562                message: format!(
563                    "cannot deserialize unresolved substitution at path {:?}: call Config::resolve() first",
564                    pv.path
565                ),
566            }),
567        }
568    }
569
570    fn deserialize_string<V: ::serde::de::Visitor<'de>>(
571        self,
572        visitor: V,
573    ) -> Result<V::Value, Self::Error> {
574        match self.value {
575            HoconValue::Scalar(sv) => visitor.visit_string(sv.raw),
576            other => Err(DeserializeError {
577                message: format!("expected string, got {:?}", other),
578            }),
579        }
580    }
581
582    fn deserialize_str<V: ::serde::de::Visitor<'de>>(
583        self,
584        visitor: V,
585    ) -> Result<V::Value, Self::Error> {
586        self.deserialize_string(visitor)
587    }
588
589    fn deserialize_bool<V: ::serde::de::Visitor<'de>>(
590        self,
591        visitor: V,
592    ) -> Result<V::Value, Self::Error> {
593        match self.value {
594            HoconValue::Scalar(sv) => match sv.raw.to_lowercase().as_str() {
595                "true" | "yes" | "on" => visitor.visit_bool(true),
596                "false" | "no" | "off" => visitor.visit_bool(false),
597                _ => Err(DeserializeError {
598                    message: format!("cannot coerce \"{}\" to bool", sv.raw),
599                }),
600            },
601            other => Err(DeserializeError {
602                message: format!("expected bool, got {:?}", other),
603            }),
604        }
605    }
606
607    fn deserialize_i64<V: ::serde::de::Visitor<'de>>(
608        self,
609        visitor: V,
610    ) -> Result<V::Value, Self::Error> {
611        match self.value {
612            HoconValue::Scalar(sv) => {
613                let n: i64 = parse_int_from_scalar(&sv, "i64")?;
614                visitor.visit_i64(n)
615            }
616            other => Err(DeserializeError {
617                message: format!("expected i64, got {:?}", other),
618            }),
619        }
620    }
621
622    fn deserialize_u64<V: ::serde::de::Visitor<'de>>(
623        self,
624        visitor: V,
625    ) -> Result<V::Value, Self::Error> {
626        match self.value {
627            HoconValue::Scalar(sv) => {
628                let n: u64 = parse_int_from_scalar(&sv, "u64")?;
629                visitor.visit_u64(n)
630            }
631            other => Err(DeserializeError {
632                message: format!("expected u64, got {:?}", other),
633            }),
634        }
635    }
636
637    fn deserialize_f64<V: ::serde::de::Visitor<'de>>(
638        self,
639        visitor: V,
640    ) -> Result<V::Value, Self::Error> {
641        match self.value {
642            HoconValue::Scalar(sv) => {
643                let f: f64 = sv.raw.parse().map_err(|_| DeserializeError {
644                    message: format!("cannot parse \"{}\" as f64", sv.raw),
645                })?;
646                visitor.visit_f64(f)
647            }
648            other => Err(DeserializeError {
649                message: format!("expected f64, got {:?}", other),
650            }),
651        }
652    }
653
654    fn deserialize_seq<V: ::serde::de::Visitor<'de>>(
655        self,
656        visitor: V,
657    ) -> Result<V::Value, Self::Error> {
658        match self.value {
659            HoconValue::Array(items) => visitor.visit_seq(HoconOwnedSeqAccess::new(items)),
660            obj @ HoconValue::Object(_) => match numeric_object_to_array(&obj) {
661                Some(items) => visitor.visit_seq(HoconOwnedSeqAccess::new(items)),
662                None => Err(DeserializeError {
663                    message: format!("expected array, got {:?}", obj),
664                }),
665            },
666            other => Err(DeserializeError {
667                message: format!("expected array, got {:?}", other),
668            }),
669        }
670    }
671
672    fn deserialize_option<V: ::serde::de::Visitor<'de>>(
673        self,
674        visitor: V,
675    ) -> Result<V::Value, Self::Error> {
676        match &self.value {
677            HoconValue::Scalar(sv) if sv.value_type == ScalarType::Null => visitor.visit_none(),
678            _ => visitor.visit_some(self),
679        }
680    }
681
682    fn deserialize_unit<V: ::serde::de::Visitor<'de>>(
683        self,
684        visitor: V,
685    ) -> Result<V::Value, Self::Error> {
686        match self.value {
687            HoconValue::Scalar(sv) if sv.value_type == ScalarType::Null => visitor.visit_unit(),
688            other => Err(DeserializeError {
689                message: format!("expected null/unit, got {:?}", other),
690            }),
691        }
692    }
693
694    fn deserialize_newtype_struct<V: ::serde::de::Visitor<'de>>(
695        self,
696        _name: &'static str,
697        visitor: V,
698    ) -> Result<V::Value, Self::Error> {
699        visitor.visit_newtype_struct(self)
700    }
701
702    fn deserialize_tuple<V: ::serde::de::Visitor<'de>>(
703        self,
704        _len: usize,
705        visitor: V,
706    ) -> Result<V::Value, Self::Error> {
707        self.deserialize_seq(visitor)
708    }
709
710    fn deserialize_tuple_struct<V: ::serde::de::Visitor<'de>>(
711        self,
712        _name: &'static str,
713        _len: usize,
714        visitor: V,
715    ) -> Result<V::Value, Self::Error> {
716        self.deserialize_seq(visitor)
717    }
718
719    fn deserialize_map<V: ::serde::de::Visitor<'de>>(
720        self,
721        visitor: V,
722    ) -> Result<V::Value, Self::Error> {
723        match self.value {
724            HoconValue::Object(map) => visitor.visit_map(OwnedHoconMapAccess::new(map)),
725            other => Err(DeserializeError {
726                message: format!("expected object, got {:?}", other),
727            }),
728        }
729    }
730
731    fn deserialize_struct<V: ::serde::de::Visitor<'de>>(
732        self,
733        _name: &'static str,
734        _fields: &'static [&'static str],
735        visitor: V,
736    ) -> Result<V::Value, Self::Error> {
737        self.deserialize_map(visitor)
738    }
739
740    fn deserialize_enum<V: ::serde::de::Visitor<'de>>(
741        self,
742        _name: &'static str,
743        _variants: &'static [&'static str],
744        visitor: V,
745    ) -> Result<V::Value, Self::Error> {
746        // Mirror HoconDeserializer::deserialize_enum (line ~345) so that converted
747        // numeric-keyed-object → array elements can deserialize as string enum variants.
748        match self.value {
749            HoconValue::Scalar(sv) => visitor.visit_enum(sv.raw.as_str().into_deserializer()),
750            other => Err(DeserializeError {
751                message: format!("expected string for enum variant, got {:?}", other),
752            }),
753        }
754    }
755
756    fn deserialize_identifier<V: ::serde::de::Visitor<'de>>(
757        self,
758        visitor: V,
759    ) -> Result<V::Value, Self::Error> {
760        self.deserialize_string(visitor)
761    }
762
763    fn deserialize_ignored_any<V: ::serde::de::Visitor<'de>>(
764        self,
765        visitor: V,
766    ) -> Result<V::Value, Self::Error> {
767        visitor.visit_unit()
768    }
769
770    ::serde::forward_to_deserialize_any! {
771        i8 i16 i32 u8 u16 u32 f32 char bytes byte_buf unit_struct
772    }
773}
774
775// ---------------------------------------------------------------------------
776// OwnedMapAccess — MapAccess that owns its IndexMap (used by OwnedHoconDeserializer)
777// ---------------------------------------------------------------------------
778
779pub(crate) struct OwnedHoconMapAccess {
780    iter: indexmap::map::IntoIter<String, HoconValue>,
781    current_value: Option<HoconValue>,
782}
783
784impl OwnedHoconMapAccess {
785    fn new(map: IndexMap<String, HoconValue>) -> Self {
786        Self {
787            iter: map.into_iter(),
788            current_value: None,
789        }
790    }
791}
792
793impl<'de> ::serde::de::MapAccess<'de> for OwnedHoconMapAccess {
794    type Error = DeserializeError;
795
796    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
797    where
798        K: ::serde::de::DeserializeSeed<'de>,
799    {
800        match self.iter.next() {
801            Some((key, value)) => {
802                self.current_value = Some(value);
803                seed.deserialize(key.into_deserializer()).map(Some)
804            }
805            None => Ok(None),
806        }
807    }
808
809    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
810    where
811        V: ::serde::de::DeserializeSeed<'de>,
812    {
813        let value = self.current_value.take().ok_or_else(|| DeserializeError {
814            message: "next_value_seed called before next_key_seed".into(),
815        })?;
816        seed.deserialize(OwnedHoconDeserializer { value })
817    }
818}