Skip to main content

toml_spanner/
de.rs

1#[cfg(test)]
2#[path = "./de_tests.rs"]
3mod tests;
4
5use std::num::NonZeroU64;
6
7use foldhash::HashMap;
8
9use crate::{
10    Arena, Error, ErrorKind, Key, Span, Table,
11    parser::{INDEXED_TABLE_THRESHOLD, KeyRef},
12    value::{self, Item},
13};
14
15/// Guides deserialization of a [`Table`] by tracking which fields have been
16/// consumed.
17///
18/// Create one via [`Root::helper`](crate::Root::helper) for the root table,
19/// or [`Item::table_helper`] / [`TableHelper::new`] for nested tables.
20/// Then extract fields with [`required`](Self::required) and
21/// [`optional`](Self::optional), and finish with
22/// [`expect_empty`](Self::expect_empty) to reject unknown keys.
23///
24/// Errors are accumulated in the shared [`Context`] rather than failing on
25/// the first problem, so a single parse pass can report multiple issues.
26///
27/// # Examples
28///
29/// ```
30/// use toml_spanner::{Arena, Deserialize, Item, Context, Failed, TableHelper};
31///
32/// struct Config {
33///     name: String,
34///     port: u16,
35///     debug: bool,
36/// }
37///
38/// impl<'de> Deserialize<'de> for Config {
39///     fn deserialize(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
40///         let mut th = value.table_helper(ctx)?;
41///         let name = th.required("name")?;
42///         let port = th.required("port")?;
43///         let debug = th.optional("debug").unwrap_or(false);
44///         th.expect_empty()?;
45///         Ok(Config { name, port, debug })
46///     }
47/// }
48/// ```
49pub struct TableHelper<'ctx, 'table, 'de> {
50    pub ctx: &'ctx mut Context<'de>,
51    pub table: &'table Table<'de>,
52    // -1 means don't use table index.
53    table_id: i32,
54    // Used for detecting unused fields or iterating over remaining for flatten into collection.
55    used_count: u32,
56    used: &'de mut FixedBitset,
57}
58
59#[repr(transparent)]
60struct FixedBitset([u64]);
61
62impl FixedBitset {
63    #[allow(clippy::mut_from_ref)]
64    pub fn new(capacity: usize, arena: &Arena) -> &mut FixedBitset {
65        let bitset_bucket_count = capacity.div_ceil(64);
66        let bitset = arena
67            .alloc(bitset_bucket_count * std::mem::size_of::<u64>())
68            .cast::<u64>();
69        for offset in 0..bitset_bucket_count {
70            // SAFETY: `bitset_len * size_of::<u64>()` bytes were allocated above,
71            // so `bitset.add(offset)` for offset in 0..bitset_len is within bounds.
72            unsafe {
73                bitset.add(offset).write(0);
74            }
75        }
76        // SAFETY: bitset points to `bitset_len` initialized u64 values in the arena.
77        let slice = unsafe { std::slice::from_raw_parts_mut(bitset.as_ptr(), bitset_bucket_count) };
78        // SAFETY: FixedBitset is #[repr(transparent)] over [u64].
79        unsafe { &mut *(slice as *mut [u64] as *mut FixedBitset) }
80    }
81
82    pub fn insert(&mut self, index: usize) -> bool {
83        let offset = index >> 6;
84        let bit = 1 << (index & 63);
85        let old = self.0[offset];
86        self.0[offset] |= bit;
87        old & bit == 0
88    }
89
90    pub fn get(&self, index: usize) -> bool {
91        let offset = index >> 6;
92        let bit = 1 << (index & 63);
93        self.0[offset] & bit != 0
94    }
95}
96
97/// An iterator over table entries that were **not** consumed by
98/// [`TableHelper::required`], [`TableHelper::optional`] or similar methods.
99///
100/// Obtained via [`TableHelper::into_remaining`].
101pub struct RemainingEntriesIter<'t, 'de> {
102    entries: &'t [(Key<'de>, Item<'de>)],
103    remaining_cells: std::slice::Iter<'de, u64>,
104    bits: u64,
105}
106impl RemainingEntriesIter<'_, '_> {
107    fn next_bucket(&mut self) -> bool {
108        if let Some(bucket) = self.remaining_cells.next() {
109            debug_assert!(self.entries.len() > 64);
110            if let Some(remaining) = self.entries.get(64..) {
111                self.entries = remaining;
112            } else {
113                // Shouldn't occur in practice, but no need to panic here.
114                return false;
115            }
116            self.bits = !*bucket;
117            true
118        } else {
119            false
120        }
121    }
122}
123
124impl<'t, 'de> Iterator for RemainingEntriesIter<'t, 'de> {
125    type Item = &'t (Key<'de>, Item<'de>);
126
127    fn next(&mut self) -> Option<Self::Item> {
128        loop {
129            if let Some(bits) = NonZeroU64::new(self.bits) {
130                let bit_index = bits.trailing_zeros() as usize;
131                self.bits &= self.bits - 1;
132                return self.entries.get(bit_index);
133            }
134            if !self.next_bucket() {
135                return None;
136            }
137        }
138    }
139}
140
141impl<'ctx, 't, 'de> TableHelper<'ctx, 't, 'de> {
142    /// Creates a new helper for the given table.
143    ///
144    /// Prefer [`Item::table_helper`] when implementing [`Deserialize`], or
145    /// [`Root::helper`](crate::Root::helper) for the root table.
146    pub fn new(ctx: &'ctx mut Context<'de>, table: &'t Table<'de>) -> Self {
147        let table_id = if table.len() > INDEXED_TABLE_THRESHOLD {
148            // Note due to 512MB limit this will fit in i32.
149            table.entries()[0].0.span.start as i32
150        } else {
151            -1
152        };
153        Self {
154            used: FixedBitset::new(table.len(), ctx.arena),
155            ctx,
156            table,
157            table_id,
158            used_count: 0,
159        }
160    }
161    /// Looks up a key-value entry without marking it as consumed.
162    ///
163    /// This is useful for peeking at a field before deciding how to
164    /// deserialize it. The entry will still be flagged as unexpected by
165    /// [`expect_empty`](Self::expect_empty) unless it is later consumed by
166    /// [`required`](Self::required) or [`optional`](Self::optional).
167    pub fn get_entry(&self, key: &str) -> Option<&'t (Key<'de>, Item<'de>)> {
168        if self.table_id < 0 {
169            for entry in self.table.entries() {
170                if entry.0.name == key {
171                    return Some(entry);
172                }
173            }
174            None
175        } else {
176            match self.ctx.index.get(&KeyRef::new(key, self.table_id as u32)) {
177                Some(index) => Some(&self.table.entries()[*index]),
178                None => None,
179            }
180        }
181    }
182
183    /// Extracts a required field and transforms it with `func`.
184    ///
185    /// Looks up `name`, marks it as consumed, and passes the [`Item`] to
186    /// `func`. This is useful for parsing string values via
187    /// [`Item::parse`] or applying custom validation without implementing
188    /// [`Deserialize`].
189    ///
190    /// # Errors
191    ///
192    /// Returns [`Failed`] if the key is absent or if `func` returns an error.
193    /// In both cases the error is pushed onto the shared [`Context`].
194    pub fn required_mapped<T>(
195        &mut self,
196        name: &'static str,
197        func: fn(&Item<'de>) -> Result<T, Error>,
198    ) -> Result<T, Failed> {
199        let Some((_, item)) = self.optional_entry(name) else {
200            return Err(self.report_missing_field(name));
201        };
202
203        func(item).map_err(|err| {
204            self.ctx.push_error(Error {
205                kind: ErrorKind::Custom(std::borrow::Cow::Owned(err.to_string())),
206                span: item.span(),
207            })
208        })
209    }
210
211    /// Extracts an optional field and transforms it with `func`.
212    ///
213    /// Returns [`None`] if the key is missing (no error recorded) or if
214    /// `func` returns an error (the error is pushed onto the [`Context`]).
215    /// The field is marked as consumed so
216    /// [`expect_empty`](Self::expect_empty) will not flag it as unexpected.
217    pub fn optional_mapped<T>(
218        &mut self,
219        name: &'static str,
220        func: fn(&Item<'de>) -> Result<T, Error>,
221    ) -> Option<T> {
222        let Some((_, item)) = self.optional_entry(name) else {
223            return None;
224        };
225
226        func(item)
227            .map_err(|err| {
228                self.ctx.push_error(Error {
229                    kind: ErrorKind::Custom(std::borrow::Cow::Owned(err.to_string())),
230                    span: item.span(),
231                })
232            })
233            .ok()
234    }
235
236    /// Returns the raw [`Item`] for a required field.
237    ///
238    /// Like [`required`](Self::required) but skips deserialization, giving
239    /// direct access to the parsed value. The field is marked as consumed.
240    ///
241    /// # Errors
242    ///
243    /// Returns [`Failed`] and records a
244    /// [`MissingField`](crate::ErrorKind::MissingField) error if the key is
245    /// absent.
246    pub fn required_item(&mut self, name: &'static str) -> Result<&'t Item<'de>, Failed> {
247        self.required_entry(name).map(|(_, value)| value)
248    }
249
250    /// Returns the raw [`Item`] for an optional field.
251    ///
252    /// Like [`optional`](Self::optional) but skips deserialization, giving
253    /// direct access to the parsed value. Returns [`None`] when the key is
254    /// missing (no error recorded). The field is marked as consumed.
255    pub fn optional_item(&mut self, name: &'static str) -> Option<&'t Item<'de>> {
256        self.optional_entry(name).map(|(_, value)| value)
257    }
258
259    /// Returns the `(`[`Key`]`, `[`Item`]`)` pair for a required field.
260    ///
261    /// Use this when you need the key's [`Span`](crate::Span) in addition to
262    /// the value. The field is marked as consumed.
263    ///
264    /// # Errors
265    ///
266    /// Returns [`Failed`] and records a
267    /// [`MissingField`](crate::ErrorKind::MissingField) error if the key is
268    /// absent.
269    pub fn required_entry(
270        &mut self,
271        name: &'static str,
272    ) -> Result<&'t (Key<'de>, Item<'de>), Failed> {
273        match self.optional_entry(name) {
274            Some(entry) => Ok(entry),
275            None => Err(self.report_missing_field(name)),
276        }
277    }
278
279    /// Returns the `(`[`Key`]`, `[`Item`]`)` pair for an optional field.
280    ///
281    /// Returns [`None`] when the key is missing (no error recorded). Use
282    /// this when you need the key's [`Span`](crate::Span) in addition to
283    /// the value. The field is marked as consumed.
284    pub fn optional_entry(&mut self, key: &str) -> Option<&'t (Key<'de>, Item<'de>)> {
285        let entry = self.get_entry(key)?;
286        let index = unsafe {
287            let ptr = entry as *const (Key<'de>, Item<'de>);
288            let base = self.table.entries().as_ptr();
289            ptr.offset_from(base) as usize
290        };
291        if self.used.insert(index) {
292            self.used_count += 1;
293        }
294        Some(entry)
295    }
296
297    #[cold]
298    fn report_missing_field(&mut self, name: &'static str) -> Failed {
299        self.ctx.errors.push(Error {
300            kind: ErrorKind::MissingField(name),
301            span: self.table.span(),
302        });
303        Failed
304    }
305
306    /// Deserializes a required field, recording an error if the key is missing.
307    ///
308    /// The field is marked as consumed so [`expect_empty`](Self::expect_empty)
309    /// will not flag it as unexpected.
310    ///
311    /// # Errors
312    ///
313    /// Returns [`Failed`] if the key is absent or if `T::deserialize` fails.
314    /// In both cases the error is pushed onto the shared [`Context`].
315    pub fn required<T: Deserialize<'de>>(&mut self, name: &'static str) -> Result<T, Failed> {
316        let Some((_, val)) = self.optional_entry(name) else {
317            return Err(self.report_missing_field(name));
318        };
319
320        T::deserialize(self.ctx, val)
321    }
322
323    /// Deserializes an optional field, returning [`None`] if the key is missing
324    /// or deserialization fails (recording the error in the [`Context`]).
325    ///
326    /// The field is marked as consumed so [`expect_empty`](Self::expect_empty)
327    /// will not flag it as unexpected.
328    pub fn optional<T: Deserialize<'de>>(&mut self, name: &str) -> Option<T> {
329        let Some((_, val)) = self.optional_entry(name) else {
330            return None;
331        };
332
333        #[allow(clippy::manual_ok_err)]
334        match T::deserialize(self.ctx, val) {
335            Ok(value) => Some(value),
336            // Note: The parent will already have recorded the error
337            Err(_) => None,
338        }
339    }
340
341    /// Returns the number of unused entries remaining in the table.
342    pub fn remaining_count(&self) -> usize {
343        self.table.len() - self.used_count as usize
344    }
345
346    /// Iterate over unused `&(Key<'de>, Item<'de>)` entries in the table.
347    pub fn into_remaining(self) -> RemainingEntriesIter<'t, 'de> {
348        let entries = self.table.entries();
349        let mut remaining_cells = self.used.0.iter();
350        RemainingEntriesIter {
351            bits: if let Some(value) = remaining_cells.next() {
352                !*value
353            } else {
354                0
355            },
356            entries,
357            remaining_cells,
358        }
359    }
360
361    /// Finishes deserialization, recording an error if any fields were not
362    /// consumed by [`required`](Self::required) or
363    /// [`optional`](Self::optional).
364    ///
365    /// Call this as the last step in a [`Deserialize`] implementation to
366    /// reject unknown keys.
367    ///
368    /// # Errors
369    ///
370    /// Returns [`Failed`] and pushes an [`ErrorKind::UnexpectedKeys`](crate::ErrorKind::UnexpectedKeys)
371    /// error if unconsumed fields remain.
372    #[inline(never)]
373    pub fn expect_empty(self) -> Result<(), Failed> {
374        if self.used_count as usize == self.table.len() {
375            return Ok(());
376        }
377
378        let mut keys = Vec::new();
379        for (i, (key, _)) in self.table.entries().iter().enumerate() {
380            if !self.used.get(i) {
381                keys.push((key.name.into(), key.span));
382            }
383        }
384
385        if keys.is_empty() {
386            return Ok(());
387        }
388
389        self.ctx.errors.push(Error::from((
390            ErrorKind::UnexpectedKeys { keys },
391            self.table.span(),
392        )));
393        Err(Failed)
394    }
395}
396
397/// Shared deserialization state that accumulates errors and holds the arena.
398///
399/// A `Context` is created by [`parse`](crate::parse) and lives inside
400/// [`Root`](crate::Root). Pass it into [`TableHelper::new`] or
401/// [`Item::table_helper`] when implementing [`Deserialize`].
402///
403/// Multiple errors can be recorded during a single deserialization pass;
404/// inspect them afterwards via [`Root::errors`](crate::Root::errors).
405pub struct Context<'de> {
406    pub arena: &'de Arena,
407    pub(crate) index: HashMap<KeyRef<'de>, usize>,
408    pub errors: Vec<Error>,
409}
410
411impl<'de> Context<'de> {
412    /// Records a "expected X, found Y" type-mismatch error and returns [`Failed`].
413    #[cold]
414    pub fn error_expected_but_found(&mut self, message: &'static str, found: &Item<'_>) -> Failed {
415        self.errors.push(Error {
416            kind: ErrorKind::Wanted {
417                expected: message,
418                found: found.type_str(),
419            },
420            span: found.span(),
421        });
422        Failed
423    }
424
425    /// Records a custom error message at the given span and returns [`Failed`].
426    #[cold]
427    pub fn error_message_at(&mut self, message: &'static str, at: Span) -> Failed {
428        self.errors.push(Error {
429            kind: ErrorKind::Custom(std::borrow::Cow::Borrowed(message)),
430            span: at,
431        });
432        Failed
433    }
434    /// Pushes a pre-built [`Error`] and returns [`Failed`].
435    #[cold]
436    pub fn push_error(&mut self, error: Error) -> Failed {
437        self.errors.push(error);
438        Failed
439    }
440
441    /// Records an out-of-range error for the type `name` and returns [`Failed`].
442    #[cold]
443    pub fn error_out_of_range(&mut self, name: &'static str, span: Span) -> Failed {
444        self.errors.push(Error {
445            kind: ErrorKind::OutOfRange(name),
446            span,
447        });
448        Failed
449    }
450}
451
452/// Sentinel indicating that a deserialization error has been recorded in the
453/// [`Context`].
454///
455/// `Failed` carries no data — the actual error details live in
456/// [`Context::errors`](Context::errors). Return `Err(Failed)` from
457/// [`Deserialize::deserialize`] after calling one of the `Context::error_*`
458/// methods.
459#[derive(Debug)]
460pub struct Failed;
461
462/// Trait for types that can be deserialized from a TOML [`Item`].
463///
464/// Implement this on your own types to enable extraction via
465/// [`TableHelper::required`] and [`TableHelper::optional`].
466/// Built-in implementations are provided for primitive types, `String`,
467/// `Vec<T>`, `Box<T>`, `Option<T>` (via `optional`), and more.
468///
469/// # Examples
470///
471/// ```
472/// use toml_spanner::{Item, Context, Deserialize, Failed, TableHelper};
473///
474/// struct Point {
475///     x: f64,
476///     y: f64,
477/// }
478///
479/// impl<'de> Deserialize<'de> for Point {
480///     fn deserialize(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
481///         let mut th = value.table_helper(ctx)?;
482///         let x = th.required("x")?;
483///         let y = th.required("y")?;
484///         th.expect_empty()?;
485///         Ok(Point { x, y })
486///     }
487/// }
488/// ```
489pub trait Deserialize<'de>: Sized {
490    /// Attempts to produce `Self` from a TOML [`Item`].
491    ///
492    /// On failure, records one or more errors in `ctx` and returns
493    /// `Err(`[`Failed`]`)`.
494    fn deserialize(ctx: &mut Context<'de>, item: &Item<'de>) -> Result<Self, Failed>;
495}
496
497impl<'de, T: Deserialize<'de>, const N: usize> Deserialize<'de> for [T; N] {
498    fn deserialize(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
499        let boxed_slice = Box::<[T]>::deserialize(ctx, value)?;
500        match <Box<[T; N]>>::try_from(boxed_slice) {
501            Ok(array) => Ok(*array),
502            Err(res) => Err(ctx.push_error(Error {
503                kind: ErrorKind::Custom(std::borrow::Cow::Owned(format!(
504                    "Expect Array Size: found {} but expected {}",
505                    res.len(),
506                    N
507                ))),
508                span: value.span(),
509            })),
510        }
511    }
512}
513
514impl<'de> Deserialize<'de> for String {
515    fn deserialize(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
516        match value.as_str() {
517            Some(s) => Ok(s.to_string()),
518            None => Err(ctx.error_expected_but_found("a string", value)),
519        }
520    }
521}
522
523impl<'de, T: Deserialize<'de>> Deserialize<'de> for Box<T> {
524    fn deserialize(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
525        match T::deserialize(ctx, value) {
526            Ok(v) => Ok(Box::new(v)),
527            Err(e) => Err(e),
528        }
529    }
530}
531impl<'de, T: Deserialize<'de>> Deserialize<'de> for Box<[T]> {
532    fn deserialize(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
533        match Vec::<T>::deserialize(ctx, value) {
534            Ok(vec) => Ok(vec.into_boxed_slice()),
535            Err(e) => Err(e),
536        }
537    }
538}
539impl<'de> Deserialize<'de> for Box<str> {
540    fn deserialize(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
541        match value.value() {
542            value::Value::String(&s) => Ok(s.into()),
543            _ => Err(ctx.error_expected_but_found("a string", value)),
544        }
545    }
546}
547impl<'de> Deserialize<'de> for &'de str {
548    fn deserialize(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
549        match value.value() {
550            value::Value::String(s) => Ok(*s),
551            _ => Err(ctx.error_expected_but_found("a string", value)),
552        }
553    }
554}
555
556impl<'de> Deserialize<'de> for std::borrow::Cow<'de, str> {
557    fn deserialize(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
558        match value.value() {
559            value::Value::String(s) => Ok(std::borrow::Cow::Borrowed(*s)),
560            _ => Err(ctx.error_expected_but_found("a string", value)),
561        }
562    }
563}
564
565impl<'de> Deserialize<'de> for bool {
566    fn deserialize(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
567        match value.as_bool() {
568            Some(b) => Ok(b),
569            None => Err(ctx.error_expected_but_found("a bool", value)),
570        }
571    }
572}
573
574fn deser_integer_ctx(
575    ctx: &mut Context<'_>,
576    value: &Item<'_>,
577    min: i64,
578    max: i64,
579    name: &'static str,
580) -> Result<i64, Failed> {
581    let span = value.span();
582    match value.as_i64() {
583        Some(i) if i >= min && i <= max => Ok(i),
584        Some(_) => Err(ctx.error_out_of_range(name, span)),
585        None => Err(ctx.error_expected_but_found("an integer", value)),
586    }
587}
588
589macro_rules! integer_new {
590    ($($num:ty),+) => {$(
591        impl<'de> Deserialize<'de> for $num {
592            fn deserialize(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
593                match deser_integer_ctx(ctx, value, <$num>::MIN as i64, <$num>::MAX as i64, stringify!($num)) {
594                    Ok(i) => Ok(i as $num),
595                    Err(e) => Err(e),
596                }
597            }
598        }
599    )+};
600}
601
602integer_new!(i8, i16, i32, isize, u8, u16, u32);
603
604impl<'de> Deserialize<'de> for i64 {
605    fn deserialize(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
606        deser_integer_ctx(ctx, value, i64::MIN, i64::MAX, "i64")
607    }
608}
609
610impl<'de> Deserialize<'de> for u64 {
611    fn deserialize(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
612        match deser_integer_ctx(ctx, value, 0, i64::MAX, "u64") {
613            Ok(i) => Ok(i as u64),
614            Err(e) => Err(e),
615        }
616    }
617}
618
619impl<'de> Deserialize<'de> for usize {
620    fn deserialize(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
621        const MAX: i64 = if usize::BITS < 64 {
622            usize::MAX as i64
623        } else {
624            i64::MAX
625        };
626        match deser_integer_ctx(ctx, value, 0, MAX, "usize") {
627            Ok(i) => Ok(i as usize),
628            Err(e) => Err(e),
629        }
630    }
631}
632
633impl<'de> Deserialize<'de> for f32 {
634    fn deserialize(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
635        match value.as_f64() {
636            Some(f) => Ok(f as f32),
637            None => Err(ctx.error_expected_but_found("a float", value)),
638        }
639    }
640}
641
642impl<'de> Deserialize<'de> for f64 {
643    fn deserialize(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
644        match value.as_f64() {
645            Some(f) => Ok(f),
646            None => Err(ctx.error_expected_but_found("a float", value)),
647        }
648    }
649}
650
651impl<'de, T> Deserialize<'de> for Vec<T>
652where
653    T: Deserialize<'de>,
654{
655    fn deserialize(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
656        let arr = value.expect_array(ctx)?;
657        let mut result = Vec::with_capacity(arr.len());
658        let mut had_error = false;
659        for item in arr {
660            match T::deserialize(ctx, item) {
661                Ok(v) => result.push(v),
662                Err(_) => had_error = true,
663            }
664        }
665        if had_error { Err(Failed) } else { Ok(result) }
666    }
667}
668
669impl<'de> Item<'de> {
670    /// Returns a string, or records an error with a custom `expected` message.
671    ///
672    /// Use this instead of [`expect_string`](Self::expect_string) when the
673    /// expected value is more specific than just "a string" — for example,
674    /// `"an IPv4 address"` or `"a hex color"`.
675    pub fn expect_custom_string(
676        &self,
677        ctx: &mut Context<'de>,
678        expected: &'static str,
679    ) -> Result<&'de str, Failed> {
680        match self.value() {
681            value::Value::String(s) => Ok(*s),
682            _ => Err(ctx.error_expected_but_found(expected, self)),
683        }
684    }
685    /// Returns a string, or records an error if this is not a string.
686    pub fn expect_string(&self, ctx: &mut Context<'de>) -> Result<&'de str, Failed> {
687        match self.value() {
688            value::Value::String(s) => Ok(*s),
689            _ => Err(ctx.error_expected_but_found("a string", self)),
690        }
691    }
692
693    /// Returns an array reference, or records an error if this is not an array.
694    pub fn expect_array(&self, ctx: &mut Context<'de>) -> Result<&crate::Array<'de>, Failed> {
695        match self.as_array() {
696            Some(arr) => Ok(arr),
697            None => Err(ctx.error_expected_but_found("an array", self)),
698        }
699    }
700
701    /// Returns a table reference, or records an error if this is not a table.
702    pub fn expect_table(&self, ctx: &mut Context<'de>) -> Result<&crate::Table<'de>, Failed> {
703        match self.as_table() {
704            Some(table) => Ok(table),
705            None => Err(ctx.error_expected_but_found("a table", self)),
706        }
707    }
708
709    /// Creates a [`TableHelper`] for this item, returning an error if it is not a table.
710    ///
711    /// This is the typical entry point for implementing [`Deserialize`].
712    pub fn table_helper<'ctx, 'item>(
713        &'item self,
714        ctx: &'ctx mut Context<'de>,
715    ) -> Result<TableHelper<'ctx, 'item, 'de>, Failed> {
716        let Some(table) = self.as_table() else {
717            return Err(ctx.error_expected_but_found("a table", self));
718        };
719        Ok(TableHelper::new(ctx, table))
720    }
721}