Skip to main content

toml_spanner/
de.rs

1#[cfg(all(test, feature = "to-toml"))]
2#[path = "./de_tests.rs"]
3mod tests;
4
5use std::collections::{BTreeMap, BTreeSet};
6use std::hash::BuildHasher;
7use std::hash::Hash;
8use std::num::NonZeroU64;
9use std::path::PathBuf;
10
11use foldhash::HashMap;
12
13use std::fmt::{self, Debug, Display};
14
15use crate::Value;
16use crate::error::ErrorInner;
17use crate::{
18    Arena, Key, Span, Table,
19    error::{Error, ErrorKind, MaybeTomlPath, PathComponent},
20    item::{self, Item},
21    parser::{INDEXED_TABLE_THRESHOLD, KeyRef},
22};
23
24/// Walks the parsed item tree and resolves uncomputed TOML paths in errors.
25///
26/// After `FromToml` runs, errors contain raw item pointers (via `TomlPath::uncomputed`).
27/// This function walks the tree to find which table entry or array element each
28/// pointer belongs to, then replaces the uncomputed path with a real one.
29pub(crate) fn compute_paths(root: &Table<'_>, errors: &mut [Error]) {
30    let mut pending: Vec<(*const u8, &mut MaybeTomlPath)> = Vec::new();
31    for error in errors.iter_mut() {
32        if error.path.is_uncomputed() {
33            pending.push((error.path.uncomputed_ptr() as *const u8, &mut error.path));
34        }
35    }
36    if pending.is_empty() {
37        return;
38    }
39
40    let mut path_stack: [PathComponent<'_>; 32] = [PathComponent::Index(0); 32];
41    compute_paths_walk(root.as_item(), &mut pending, &mut path_stack, 0);
42}
43
44fn compute_paths_walk<'de>(
45    item: &Item<'de>,
46    pending: &mut Vec<(*const u8, &mut MaybeTomlPath)>,
47    path_stack: &mut [PathComponent<'de>; 32],
48    path_depth: usize,
49) {
50    if path_depth >= path_stack.len() {
51        return;
52    }
53    match item.value() {
54        Value::Table(table) => {
55            let entries = table.entries();
56            if entries.is_empty() {
57                return;
58            }
59            let entry_size = std::mem::size_of::<(Key<'_>, Item<'_>)>();
60            let base = entries.as_ptr() as *const u8;
61            // SAFETY: entries is a valid slice; pointer arithmetic stays in bounds.
62            let end = unsafe { base.add(entries.len() * entry_size) };
63
64            let mut i = 0;
65            while let Some((ptr, path)) = pending.get_mut(i) {
66                let ptr: *const u8 = *ptr;
67                // SAFETY: base+key_size is the address of the first Item in entries.
68                if ptr >= base && ptr < end {
69                    let byte_offset = unsafe { ptr.byte_offset_from(base) } as usize;
70                    let entry_index = byte_offset / entry_size;
71                    if entry_index < entries.len() {
72                        path_stack[path_depth] = PathComponent::Key(entries[entry_index].0);
73                        **path = MaybeTomlPath::from_components(&path_stack[..path_depth + 1]);
74                        pending.swap_remove(i);
75                        continue;
76                    }
77                }
78                i += 1;
79            }
80
81            for (key, child) in table {
82                path_stack[path_depth] = PathComponent::Key(*key);
83                compute_paths_walk(child, pending, path_stack, path_depth + 1);
84            }
85        }
86        Value::Array(array) => {
87            let slice = array.as_slice();
88            if slice.is_empty() {
89                return;
90            }
91            let item_size = std::mem::size_of::<Item<'_>>();
92            let base = slice.as_ptr() as *const u8;
93            // SAFETY: slice is a valid slice; pointer arithmetic stays in bounds.
94            let end = unsafe { base.add(slice.len() * item_size) };
95
96            let mut i = 0;
97            while let Some((ptr, path)) = pending.get_mut(i) {
98                let ptr = *ptr;
99                if ptr >= base && ptr < end {
100                    let byte_offset = unsafe { ptr.byte_offset_from(base) } as usize;
101                    let elem_index = byte_offset / item_size;
102                    if elem_index < slice.len() {
103                        path_stack[path_depth] = PathComponent::Index(elem_index);
104                        **path = MaybeTomlPath::from_components(&path_stack[..path_depth + 1]);
105                        pending.swap_remove(i);
106                        continue;
107                    }
108                }
109                i += 1;
110            }
111
112            let mut idx = 0; // For some reason more efficient then iter() enumerate()
113            for child in array {
114                path_stack[path_depth] = PathComponent::Index(idx);
115                compute_paths_walk(child, pending, path_stack, path_depth + 1);
116                idx += 1;
117            }
118        }
119        _ => (),
120    }
121}
122
123/// Guides extraction from a [`Table`] by tracking which fields have been
124/// consumed.
125///
126/// Create via [`Document::table_helper`](crate::Document::table_helper) for the root
127/// table, or [`Item::table_helper`] / [`TableHelper::new`] for nested
128/// tables. Extract fields with [`required`](Self::required) and
129/// [`optional`](Self::optional), then call
130/// [`require_empty`](Self::require_empty) to reject unknown keys.
131///
132/// Errors accumulate in the shared [`Context`] rather than failing on the
133/// first problem, so a single pass can report multiple issues.
134///
135/// # Examples
136///
137/// ```
138/// use toml_spanner::{Arena, FromToml, Item, Context, Failed, TableHelper};
139///
140/// struct Config {
141///     name: String,
142///     port: u16,
143///     debug: bool,
144/// }
145///
146/// impl<'de> FromToml<'de> for Config {
147///     fn from_toml(ctx: &mut Context<'de>, item: &Item<'de>) -> Result<Self, Failed> {
148///         let mut th = item.table_helper(ctx)?;
149///         let name = th.required("name")?;
150///         let port = th.required("port")?;
151///         let debug = th.optional("debug").unwrap_or(false);
152///         th.require_empty()?;
153///         Ok(Config { name, port, debug })
154///     }
155/// }
156/// ```
157pub struct TableHelper<'ctx, 'table, 'de> {
158    pub ctx: &'ctx mut Context<'de>,
159    pub table: &'table Table<'de>,
160    // -1 means don't use table index.
161    table_id: i32,
162    // Used for detecting unused fields or iterating over remaining for flatten into collection.
163    used_count: u32,
164    used: &'de mut FixedBitset,
165}
166
167#[repr(transparent)]
168struct FixedBitset([u64]);
169
170impl FixedBitset {
171    #[allow(clippy::mut_from_ref)]
172    pub fn new(capacity: usize, arena: &Arena) -> &mut FixedBitset {
173        let bitset_bucket_count = capacity.div_ceil(64);
174        let bitset = arena
175            .alloc(bitset_bucket_count * std::mem::size_of::<u64>())
176            .cast::<u64>();
177        for offset in 0..bitset_bucket_count {
178            // SAFETY: `bitset_len * size_of::<u64>()` bytes were allocated above,
179            // so `bitset.add(offset)` for offset in 0..bitset_len is within bounds.
180            unsafe {
181                bitset.add(offset).write(0);
182            }
183        }
184        // SAFETY: bitset points to `bitset_len` initialized u64 values in the arena.
185        let slice = unsafe { std::slice::from_raw_parts_mut(bitset.as_ptr(), bitset_bucket_count) };
186        // SAFETY: FixedBitset is #[repr(transparent)] over [u64].
187        unsafe { &mut *(slice as *mut [u64] as *mut FixedBitset) }
188    }
189
190    pub fn insert(&mut self, index: usize) -> bool {
191        let offset = index >> 6;
192        let bit = 1 << (index & 63);
193        let old = self.0[offset];
194        self.0[offset] |= bit;
195        old & bit == 0
196    }
197
198    pub fn get(&self, index: usize) -> bool {
199        let offset = index >> 6;
200        let bit = 1 << (index & 63);
201        self.0[offset] & bit != 0
202    }
203}
204
205/// An iterator over table entries that were **not** consumed by
206/// [`TableHelper::required`], [`TableHelper::optional`] or similar methods.
207///
208/// Obtained via [`TableHelper::into_remaining`].
209pub struct RemainingEntriesIter<'t, 'de> {
210    entries: &'t [(Key<'de>, Item<'de>)],
211    remaining_cells: std::slice::Iter<'de, u64>,
212    bits: u64,
213}
214impl RemainingEntriesIter<'_, '_> {
215    fn next_bucket(&mut self) -> bool {
216        let Some(bucket) = self.remaining_cells.next() else {
217            return false;
218        };
219        debug_assert!(self.entries.len() > 64);
220        let Some(remaining) = self.entries.get(64..) else {
221            return false;
222        };
223        self.entries = remaining;
224        self.bits = !*bucket;
225        true
226    }
227}
228
229impl<'t, 'de> Iterator for RemainingEntriesIter<'t, 'de> {
230    type Item = &'t (Key<'de>, Item<'de>);
231
232    fn next(&mut self) -> Option<Self::Item> {
233        loop {
234            if let Some(bits) = NonZeroU64::new(self.bits) {
235                let bit_index = bits.trailing_zeros() as usize;
236                self.bits &= self.bits - 1;
237                return self.entries.get(bit_index);
238            }
239            if !self.next_bucket() {
240                return None;
241            }
242        }
243    }
244}
245
246impl<'ctx, 't, 'de> TableHelper<'ctx, 't, 'de> {
247    /// Creates a new helper for the given table.
248    ///
249    /// Prefer [`Item::table_helper`] inside [`FromToml`] implementations, or
250    /// [`Document::table_helper`](crate::Document::table_helper) for the root table.
251    pub fn new(ctx: &'ctx mut Context<'de>, table: &'t Table<'de>) -> Self {
252        let table_id = if table.len() > INDEXED_TABLE_THRESHOLD && table.meta.is_span_mode() {
253            table.entries()[0].0.span.start as i32
254        } else {
255            -1
256        };
257        Self {
258            used: FixedBitset::new(table.len(), ctx.arena),
259            ctx,
260            table,
261            table_id,
262            used_count: 0,
263        }
264    }
265
266    /// Looks up a key-value entry without marking it as consumed.
267    ///
268    /// Useful for peeking at a field before deciding how to convert it.
269    /// The entry will still be flagged as unexpected by
270    /// [`require_empty`](Self::require_empty) unless later consumed by
271    /// [`required`](Self::required) or [`optional`](Self::optional).
272    pub fn get_entry(&self, key: &str) -> Option<&'t (Key<'de>, Item<'de>)> {
273        if self.table_id < 0 {
274            for entry in self.table.entries() {
275                if entry.0.name == key {
276                    return Some(entry);
277                }
278            }
279            None
280        } else {
281            match self.ctx.index.get(&KeyRef::new(key, self.table_id as u32)) {
282                Some(index) => Some(&self.table.entries()[*index]),
283                None => None,
284            }
285        }
286    }
287
288    /// Extracts a required field and transforms it with `func`.
289    ///
290    /// Looks up `name`, marks it as consumed, and passes the [`Item`] to
291    /// `func`. Useful for parsing string values via [`Item::parse`] or
292    /// applying custom validation without implementing [`FromToml`].
293    ///
294    /// # Errors
295    ///
296    /// Returns [`Failed`] if the key is absent or if `func` returns an error.
297    /// In both cases the error is pushed onto the shared [`Context`].
298    pub fn required_mapped<T>(
299        &mut self,
300        name: &'static str,
301        func: fn(&Item<'de>) -> Result<T, Error>,
302    ) -> Result<T, Failed> {
303        let Some((_, item)) = self.optional_entry(name) else {
304            return Err(self.report_missing_field(name));
305        };
306
307        match func(item) {
308            Ok(t) => Ok(t),
309            Err(e) => Err(self.ctx.push_error(Error::custom(e, item.span_unchecked()))),
310        }
311    }
312
313    /// Extracts an optional field and transforms it with `func`.
314    ///
315    /// Returns [`None`] if the key is missing (no error recorded) or if
316    /// `func` returns an error (the error is pushed onto the [`Context`]).
317    /// The field is marked as consumed so
318    /// [`require_empty`](Self::require_empty) will not flag it as unexpected.
319    pub fn optional_mapped<T>(
320        &mut self,
321        name: &'static str,
322        func: fn(&Item<'de>) -> Result<T, Error>,
323    ) -> Option<T> {
324        let Some((_, item)) = self.optional_entry(name) else {
325            return None;
326        };
327
328        match func(item) {
329            Ok(t) => Some(t),
330            Err(e) => {
331                self.ctx.push_error(Error::custom(e, item.span_unchecked()));
332                None
333            }
334        }
335    }
336
337    /// Returns the raw [`Item`] for a required field.
338    ///
339    /// Like [`required`](Self::required) but skips conversion, giving direct
340    /// access to the parsed value. The field is marked as consumed.
341    ///
342    /// # Errors
343    ///
344    /// Returns [`Failed`] and records a
345    /// [`MissingField`](crate::ErrorKind::MissingField) error if the key is
346    /// absent.
347    pub fn required_item(&mut self, name: &'static str) -> Result<&'t Item<'de>, Failed> {
348        if let Ok((_, item)) = self.required_entry(name) {
349            Ok(item)
350        } else {
351            Err(self.report_missing_field(name))
352        }
353    }
354
355    /// Returns the raw [`Item`] for an optional field.
356    ///
357    /// Like [`optional`](Self::optional) but skips conversion, giving direct
358    /// access to the parsed value. Returns [`None`] when the key is missing
359    /// (no error recorded). The field is marked as consumed.
360    pub fn optional_item(&mut self, name: &'static str) -> Option<&'t Item<'de>> {
361        if let Some((_, item)) = self.optional_entry(name) {
362            Some(item)
363        } else {
364            None
365        }
366    }
367
368    /// Returns the `(`[`Key`]`, `[`Item`]`)` pair for a required field.
369    ///
370    /// Useful when the key's [`Span`](crate::Span) is needed in addition to
371    /// the value. The field is marked as consumed.
372    ///
373    /// # Errors
374    ///
375    /// Returns [`Failed`] and records a
376    /// [`MissingField`](crate::ErrorKind::MissingField) error if the key is
377    /// absent.
378    pub fn required_entry(
379        &mut self,
380        name: &'static str,
381    ) -> Result<&'t (Key<'de>, Item<'de>), Failed> {
382        match self.optional_entry(name) {
383            Some(entry) => Ok(entry),
384            None => Err(self.report_missing_field(name)),
385        }
386    }
387
388    /// Returns the `(`[`Key`]`, `[`Item`]`)` pair for an optional field.
389    ///
390    /// Returns [`None`] when the key is missing (no error recorded). Useful
391    /// when the key's [`Span`](crate::Span) is needed in addition to the
392    /// value. The field is marked as consumed.
393    pub fn optional_entry(&mut self, key: &str) -> Option<&'t (Key<'de>, Item<'de>)> {
394        let Some(entry) = self.get_entry(key) else {
395            return None;
396        };
397        // SAFETY: `entry` was returned by get_entry(), which either performs a
398        // linear scan of self.table.entries() or indexes into that same slice
399        // via the hash index. In both cases `entry` points to an element within
400        // the slice whose base pointer is `base`. offset_from is valid because
401        // both pointers derive from the same allocation and the result is a
402        // non-negative element index (< table.len()).
403        let index = unsafe {
404            let ptr = entry as *const (Key<'de>, Item<'de>);
405            let base = self.table.entries().as_ptr();
406            ptr.offset_from(base) as usize
407        };
408        if self.used.insert(index) {
409            self.used_count += 1;
410        }
411        Some(entry)
412    }
413
414    #[cold]
415    fn report_missing_field(&mut self, name: &'static str) -> Failed {
416        self.ctx.errors.push(Error::new_with_path(
417            ErrorKind::MissingField(name),
418            self.table.span(),
419            MaybeTomlPath::uncomputed(self.table.as_item()),
420        ));
421        Failed
422    }
423
424    /// Extracts and converts a required field via [`FromToml`].
425    ///
426    /// The field is marked as consumed so [`require_empty`](Self::require_empty)
427    /// will not flag it as unexpected.
428    ///
429    /// # Errors
430    ///
431    /// Returns [`Failed`] if the key is absent or if conversion fails.
432    /// In both cases the error is pushed onto the shared [`Context`].
433    pub fn required<T: FromToml<'de>>(&mut self, name: &'static str) -> Result<T, Failed> {
434        let Some((_, val)) = self.optional_entry(name) else {
435            return Err(self.report_missing_field(name));
436        };
437
438        T::from_toml(self.ctx, val)
439    }
440
441    /// Extracts and converts an optional field via [`FromToml`], returning
442    /// [`None`] if the key is missing or conversion fails (recording the
443    /// error in the [`Context`]).
444    ///
445    /// The field is marked as consumed so [`require_empty`](Self::require_empty)
446    /// will not flag it as unexpected.
447    pub fn optional<T: FromToml<'de>>(&mut self, name: &str) -> Option<T> {
448        let Some((_, val)) = self.optional_entry(name) else {
449            return None;
450        };
451
452        #[allow(clippy::manual_ok_err)]
453        match T::from_toml(self.ctx, val) {
454            Ok(value) => Some(value),
455            Err(_) => None,
456        }
457    }
458
459    /// Returns the number of unused entries remaining in the table.
460    pub fn remaining_count(&self) -> usize {
461        self.table.len() - self.used_count as usize
462    }
463
464    /// Iterate over unused `&(Key<'de>, Item<'de>)` entries in the table.
465    pub fn into_remaining(self) -> RemainingEntriesIter<'t, 'de> {
466        let entries = self.table.entries();
467        let mut remaining_cells = self.used.0.iter();
468        RemainingEntriesIter {
469            bits: if let Some(value) = remaining_cells.next() {
470                !*value
471            } else {
472                0
473            },
474            entries,
475            remaining_cells,
476        }
477    }
478
479    /// Finishes field extraction, recording an error for any fields not
480    /// consumed by [`required`](Self::required) or
481    /// [`optional`](Self::optional).
482    ///
483    /// Call as the last step in a [`FromToml`] implementation to reject
484    /// unknown keys.
485    ///
486    /// # Errors
487    ///
488    /// Returns [`Failed`] and pushes an [`ErrorKind::UnexpectedKey`](crate::ErrorKind::UnexpectedKey)
489    /// error if unconsumed fields remain.
490    #[doc(alias = "expect_empty")]
491    #[inline(never)]
492    pub fn require_empty(self) -> Result<(), Failed> {
493        if self.used_count as usize == self.table.len() {
494            return Ok(());
495        }
496
497        let mut had_unexpected = false;
498        for (i, (key, item)) in self.table.entries().iter().enumerate() {
499            if !self.used.get(i) {
500                self.ctx.errors.push(Error {
501                    kind: ErrorInner::Static(ErrorKind::UnexpectedKey { tag: 0 }),
502                    span: key.span,
503                    path: MaybeTomlPath::uncomputed(item),
504                });
505
506                had_unexpected = true;
507            }
508        }
509
510        if had_unexpected { Err(Failed) } else { Ok(()) }
511    }
512}
513
514/// Shared state that accumulates errors and holds the arena.
515///
516/// Created by [`parse`](crate::parse) and stored inside
517/// [`Document`](crate::Document). Pass it into [`TableHelper::new`] or
518/// [`Item::table_helper`] when implementing [`FromToml`].
519///
520/// Multiple errors can be recorded during a single conversion pass.
521/// Inspect them via [`Document::errors`](crate::Document::errors).
522pub struct Context<'de> {
523    pub arena: &'de Arena,
524    pub(crate) index: HashMap<KeyRef<'de>, usize>,
525    pub errors: Vec<Error>,
526    pub(crate) source: &'de str,
527}
528
529impl<'de> Context<'de> {
530    /// Returns the original TOML source string passed to [`parse`](crate::parse).
531    pub fn source(&self) -> &'de str {
532        self.source
533    }
534
535    /// Records a "expected X, found Y" type-mismatch error and returns [`Failed`].
536    #[cold]
537    pub fn report_expected_but_found(
538        &mut self,
539        message: &'static &'static str,
540        found: &Item<'de>,
541    ) -> Failed {
542        let path = MaybeTomlPath::uncomputed(found);
543        self.errors.push(Error::new_with_path(
544            ErrorKind::Wanted {
545                expected: message,
546                found: found.type_str(),
547            },
548            found.span(),
549            path,
550        ));
551        Failed
552    }
553
554    /// Records an "unknown variant" error listing the accepted variants and returns [`Failed`].
555    #[cold]
556    pub fn report_unexpected_variant(
557        &mut self,
558        expected: &'static [&'static str],
559        found: &Item<'de>,
560    ) -> Failed {
561        let path = MaybeTomlPath::uncomputed(found);
562        self.errors.push(Error::new_with_path(
563            ErrorKind::UnexpectedVariant { expected },
564            found.span(),
565            path,
566        ));
567        Failed
568    }
569
570    /// Records a custom error message at the given span and returns [`Failed`].
571    #[cold]
572    pub fn report_error_at(&mut self, message: &'static str, at: Span) -> Failed {
573        self.errors.push(Error::custom_static(message, at));
574        Failed
575    }
576    /// Pushes a pre-built [`Error`] and returns [`Failed`].
577    #[cold]
578    pub fn push_error(&mut self, error: Error) -> Failed {
579        self.errors.push(error);
580        Failed
581    }
582
583    /// Records a custom error from a [`ToString`] value and returns [`Failed`].
584    #[cold]
585    pub fn report_custom_error(&mut self, error: impl ToString, item: &Item<'de>) -> Failed {
586        self.push_error(Error::custom(error, item.span()))
587    }
588
589    /// Records an out-of-range error for the type `name` and returns [`Failed`].
590    #[cold]
591    pub fn report_out_of_range(
592        &mut self,
593        ty: &'static &'static str,
594        range: &'static &'static str,
595        found: &Item<'de>,
596    ) -> Failed {
597        let path = MaybeTomlPath::uncomputed(found);
598        self.errors.push(Error::new_with_path(
599            ErrorKind::OutOfRange { ty, range },
600            found.span(),
601            path,
602        ));
603        Failed
604    }
605
606    /// Records a missing-field error and returns [`Failed`].
607    ///
608    /// Used by generated `FromToml` implementations that iterate over table
609    /// entries instead of using [`TableHelper`].
610    #[cold]
611    pub fn report_missing_field(&mut self, name: &'static str, item: &Item<'de>) -> Failed {
612        let path = MaybeTomlPath::uncomputed(item);
613        self.errors.push(Error::new_with_path(
614            ErrorKind::MissingField(name),
615            item.span(),
616            path,
617        ));
618        Failed
619    }
620
621    /// Records a duplicate-field error and returns [`Failed`].
622    ///
623    /// Used by generated `FromToml` implementations when a field with aliases
624    /// is set more than once (e.g. both the primary key and an alias appear).
625    #[cold]
626    pub fn report_duplicate_field(
627        &mut self,
628        name: &'static str,
629        key_span: Span,
630        first_key_span: Span,
631        item: &Item<'de>,
632    ) -> Failed {
633        self.push_error(Error::new_with_path(
634            ErrorKind::DuplicateField {
635                field: name,
636                first: first_key_span,
637            },
638            key_span,
639            MaybeTomlPath::uncomputed(item),
640        ))
641    }
642
643    /// Records a deprecated-field warning with TOML path information.
644    ///
645    /// Unlike other `report_*` methods this is **non-fatal**: it pushes
646    /// an error but does not return [`Failed`], so deserialization continues.
647    #[cold]
648    pub fn report_deprecated_field(
649        &mut self,
650        tag: u32,
651        old: &'static &'static str,
652        new: &'static &'static str,
653        key_span: Span,
654        item: &Item<'de>,
655    ) {
656        self.errors.push(Error::new_with_path(
657            ErrorKind::Deprecated { tag, old, new },
658            key_span,
659            MaybeTomlPath::uncomputed(item),
660        ));
661    }
662
663    /// Records an unexpected-key error with TOML path information.
664    #[cold]
665    pub fn report_unexpected_key(&mut self, tag: u32, item: &Item<'de>, key_span: Span) -> Failed {
666        let path = MaybeTomlPath::uncomputed(item);
667        self.errors.push(Error::new_with_path(
668            ErrorKind::UnexpectedKey { tag },
669            key_span,
670            path,
671        ));
672        Failed
673    }
674}
675
676pub use crate::Failed;
677
678/// Converts a TOML [`Item`] into a Rust type.
679///
680/// `#[derive(Toml)]` generates `FromToml` by default, or add
681/// `#[toml(FromToml)]` to be explicit (required when also deriving
682/// `ToToml`). See the [`Toml`](macro@crate::Toml) derive macro for the full
683/// set of attributes, enum representations, and field options.
684///
685/// # Examples
686///
687/// [`from_str`](crate::from_str) parses and deserializes when all fields
688/// are owned:
689///
690#[cfg_attr(feature = "derive", doc = "```")]
691#[cfg_attr(not(feature = "derive"), doc = "```ignore")]
692/// use toml_spanner::Toml;
693///
694/// #[derive(Toml)]
695/// struct Config {
696///     name: String,
697///     port: u16,
698/// }
699///
700/// let config: Config = toml_spanner::from_str("name = 'app'\nport = 8080").unwrap();
701/// ```
702///
703/// Parse into a [`Document`](crate::Document) when fields borrow from the
704/// input. The `'de` lifetime spans both the source text and the [`Arena`],
705/// so `&'de str` fields work even when the value contains escape sequences
706/// (the arena stores the decoded string).
707///
708#[cfg_attr(feature = "derive", doc = "```")]
709#[cfg_attr(not(feature = "derive"), doc = "```ignore")]
710/// use toml_spanner::{Arena, parse, Toml};
711///
712/// #[derive(Toml)]
713/// struct Package<'a> {
714///     name: &'a str,
715///     version: &'a str,
716/// }
717///
718/// let arena = Arena::new();
719/// let mut doc = parse("name = 'my-pkg'\nversion = '1.0'", &arena).unwrap();
720/// let pkg: Package<'_> = doc.to().unwrap();
721/// assert_eq!(pkg.name, "my-pkg");
722/// ```
723///
724/// Manual implementation with [`TableHelper`]:
725///
726/// ```
727/// use toml_spanner::{Item, Context, FromToml, Failed};
728///
729/// struct Point {
730///     x: f64,
731///     y: f64,
732/// }
733///
734/// impl<'de> FromToml<'de> for Point {
735///     fn from_toml(ctx: &mut Context<'de>, item: &Item<'de>) -> Result<Self, Failed> {
736///         let mut th = item.table_helper(ctx)?;
737///         let x = th.required("x")?;
738///         let y = th.required("y")?;
739///         th.require_empty()?;
740///         Ok(Point { x, y })
741///     }
742/// }
743/// ```
744///
745/// [`require_empty`](TableHelper::require_empty) rejects unknown keys by
746/// returning [`Failed`]. The derive defaults to recording them as warnings
747/// instead, a distinction covered below.
748///
749/// # Error handling
750///
751/// [`Failed`] is a zero size sentinel, not an error type. All actual errors
752/// are recorded in the shared [`Context`], which collects every problem
753/// across the entire pass so they can be reported together. Use the
754/// `report_*` methods on [`Context`] (such as
755/// [`report_custom_error`](Context::report_custom_error)) to record an
756/// error and receive a [`Failed`] to return. Always push at least one error
757/// before returning `Err(Failed)`.
758///
759/// An implementation can also push errors while still returning `Ok`,
760/// recording problems without preventing construction. This is how the
761/// derive handles unknown keys by default (`warn_unknown_fields`).
762/// [`Document::to`](crate::Document::to) treats any recorded error as
763/// fatal, returning `Err` even when `from_toml` succeeded.
764/// [`Document::to_allowing_errors`](crate::Document::to_allowing_errors)
765/// returns both the value and the accumulated errors, letting the caller
766/// decide which are acceptable.
767///
768/// For untagged enums, the derive snapshots the error count before
769/// attempting each variant and truncates on failure, so only errors from
770/// the matching (or final) variant are reported.
771pub trait FromToml<'de>: Sized {
772    /// Attempts to construct `Self` from a TOML [`Item`].
773    fn from_toml(ctx: &mut Context<'de>, item: &Item<'de>) -> Result<Self, Failed>;
774}
775
776/// Trait for types that can be constructed from flattened TOML table entries.
777///
778/// Used with `#[toml(flatten)]` on struct fields. Built-in implementations
779/// cover `HashMap` and `BTreeMap`.
780///
781/// If your type implements [`FromToml`], use
782/// `#[toml(flatten, with = flatten_any)]` in your derive instead of
783/// implementing this trait. See [`helper::flatten_any`](crate::helper::flatten_any).
784#[diagnostic::on_unimplemented(
785    message = "`{Self}` does not implement `FromFlattened`",
786    note = "if `{Self}` implements `FromToml`, you can use `#[toml(flatten, with = flatten_any)]` instead of a manual `FromFlattened` impl"
787)]
788pub trait FromFlattened<'de>: Sized {
789    /// Intermediate accumulator type used during conversion.
790    type Partial;
791    /// Creates an empty accumulator to collect flattened entries.
792    fn init() -> Self::Partial;
793    /// Inserts a single key-value pair into the accumulator.
794    fn insert(
795        ctx: &mut Context<'de>,
796        key: &Key<'de>,
797        item: &Item<'de>,
798        partial: &mut Self::Partial,
799    ) -> Result<(), Failed>;
800    /// Converts the accumulator into the final value after all entries
801    /// have been inserted. The `parent` parameter is the table that
802    /// contained the flattened entries.
803    fn finish(
804        ctx: &mut Context<'de>,
805        parent: &Table<'de>,
806        partial: Self::Partial,
807    ) -> Result<Self, Failed>;
808}
809
810/// Converts a TOML key into a map key, preserving span information.
811fn key_from_toml<'de, K: FromToml<'de>>(
812    ctx: &mut Context<'de>,
813    key: &Key<'de>,
814) -> Result<K, Failed> {
815    let item = Item::string_spanned(key.name, key.span);
816    K::from_toml(ctx, &item)
817}
818
819impl<'de, K, V, H> FromFlattened<'de> for std::collections::HashMap<K, V, H>
820where
821    K: Hash + Eq + FromToml<'de>,
822    V: FromToml<'de>,
823    H: Default + BuildHasher,
824{
825    type Partial = Self;
826    fn init() -> Self {
827        std::collections::HashMap::default()
828    }
829    fn insert(
830        ctx: &mut Context<'de>,
831        key: &Key<'de>,
832        item: &Item<'de>,
833        partial: &mut Self::Partial,
834    ) -> Result<(), Failed> {
835        let k = key_from_toml(ctx, key)?;
836        let v = match V::from_toml(ctx, item) {
837            Ok(v) => v,
838            Err(_) => return Err(Failed),
839        };
840        partial.insert(k, v);
841        Ok(())
842    }
843    fn finish(
844        _ctx: &mut Context<'de>,
845        _parent: &Table<'de>,
846        partial: Self::Partial,
847    ) -> Result<Self, Failed> {
848        Ok(partial)
849    }
850}
851
852impl<'de, K, V, H> FromToml<'de> for std::collections::HashMap<K, V, H>
853where
854    K: Hash + Eq + FromToml<'de>,
855    V: FromToml<'de>,
856    H: Default + BuildHasher,
857{
858    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
859        let table = value.require_table(ctx)?;
860        let mut map = std::collections::HashMap::default();
861        let mut had_error = false;
862        for (key, item) in table {
863            let k = match key_from_toml(ctx, key) {
864                Ok(k) => k,
865                Err(_) => {
866                    had_error = true;
867                    continue;
868                }
869            };
870            match V::from_toml(ctx, item) {
871                Ok(v) => {
872                    map.insert(k, v);
873                }
874                Err(_) => had_error = true,
875            }
876        }
877        if had_error { Err(Failed) } else { Ok(map) }
878    }
879}
880
881impl<'de, K, V> FromFlattened<'de> for BTreeMap<K, V>
882where
883    K: Ord + FromToml<'de>,
884    V: FromToml<'de>,
885{
886    type Partial = Self;
887    fn init() -> Self {
888        BTreeMap::new()
889    }
890    fn insert(
891        ctx: &mut Context<'de>,
892        key: &Key<'de>,
893        item: &Item<'de>,
894        partial: &mut Self::Partial,
895    ) -> Result<(), Failed> {
896        let k = key_from_toml(ctx, key)?;
897        let v = match V::from_toml(ctx, item) {
898            Ok(v) => v,
899            Err(_) => return Err(Failed),
900        };
901        partial.insert(k, v);
902        Ok(())
903    }
904    fn finish(
905        _ctx: &mut Context<'de>,
906        _parent: &Table<'de>,
907        partial: Self::Partial,
908    ) -> Result<Self, Failed> {
909        Ok(partial)
910    }
911}
912
913impl<'de, T: FromToml<'de>, const N: usize> FromToml<'de> for [T; N] {
914    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
915        let boxed_slice = Box::<[T]>::from_toml(ctx, value)?;
916        match <Box<[T; N]>>::try_from(boxed_slice) {
917            Ok(array) => Ok(*array),
918            Err(res) => Err(ctx.push_error(Error::custom(
919                format!(
920                    "expected an array with a size of {}, found one with a size of {}",
921                    N,
922                    res.len()
923                ),
924                value.span_unchecked(),
925            ))),
926        }
927    }
928}
929
930macro_rules! impl_from_toml_tuple {
931    ($len:expr, $($idx:tt => $T:ident, $var:ident),+) => {
932        impl<'de, $($T: FromToml<'de>),+> FromToml<'de> for ($($T,)+) {
933            fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
934                let arr = value.require_array(ctx)?;
935                if arr.len() != $len {
936                    return Err(ctx.push_error(Error::custom(
937                        format!(
938                            "expected an array with a size of {}, found one with a size of {}",
939                            $len,
940                            arr.len()
941                        ),
942                        value.span_unchecked(),
943                    )));
944                }
945                let slice = arr.as_slice();
946                let mut had_error = false;
947                $(
948                    let $var = match $T::from_toml(ctx, &slice[$idx]) {
949                        Ok(v) => Some(v),
950                        Err(_) => { had_error = true; None }
951                    };
952                )+
953                if had_error {
954                    return Err(Failed);
955                }
956                Ok(($($var.unwrap(),)+))
957            }
958        }
959    };
960}
961
962impl_from_toml_tuple!(1, 0 => A, a);
963impl_from_toml_tuple!(2, 0 => A, a, 1 => B, b);
964impl_from_toml_tuple!(3, 0 => A, a, 1 => B, b, 2 => C, c);
965
966impl<'de> FromToml<'de> for String {
967    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
968        match value.as_str() {
969            Some(s) => Ok(s.to_string()),
970            None => Err(ctx.report_expected_but_found(&"a string", value)),
971        }
972    }
973}
974
975impl<'de> FromToml<'de> for PathBuf {
976    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
977        match value.as_str() {
978            Some(s) => Ok(PathBuf::from(s)),
979            None => Err(ctx.report_expected_but_found(&"a path", value)),
980        }
981    }
982}
983
984impl<'de, T: FromToml<'de>> FromToml<'de> for Option<T> {
985    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
986        T::from_toml(ctx, value).map(Some)
987    }
988}
989
990impl<'de, T: FromToml<'de>> FromToml<'de> for Box<T> {
991    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
992        match T::from_toml(ctx, value) {
993            Ok(v) => Ok(Box::new(v)),
994            Err(e) => Err(e),
995        }
996    }
997}
998impl<'de, T: FromToml<'de>> FromToml<'de> for Box<[T]> {
999    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1000        match Vec::<T>::from_toml(ctx, value) {
1001            Ok(vec) => Ok(vec.into_boxed_slice()),
1002            Err(e) => Err(e),
1003        }
1004    }
1005}
1006impl<'de> FromToml<'de> for Box<str> {
1007    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1008        match value.value() {
1009            item::Value::String(&s) => Ok(s.into()),
1010            _ => Err(ctx.report_expected_but_found(&"a string", value)),
1011        }
1012    }
1013}
1014impl<'de> FromToml<'de> for &'de str {
1015    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1016        match value.value() {
1017            item::Value::String(s) => Ok(*s),
1018            _ => Err(ctx.report_expected_but_found(&"a string", value)),
1019        }
1020    }
1021}
1022
1023impl<'de> FromToml<'de> for std::borrow::Cow<'de, str> {
1024    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1025        match value.value() {
1026            item::Value::String(s) => Ok(std::borrow::Cow::Borrowed(*s)),
1027            _ => Err(ctx.report_expected_but_found(&"a string", value)),
1028        }
1029    }
1030}
1031
1032impl<'de> FromToml<'de> for bool {
1033    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1034        match value.as_bool() {
1035            Some(b) => Ok(b),
1036            None => Err(ctx.report_expected_but_found(&"a bool", value)),
1037        }
1038    }
1039}
1040
1041fn deser_integer_ctx<'de>(
1042    ctx: &mut Context<'de>,
1043    value: &Item<'de>,
1044    min: i128,
1045    max: i128,
1046    ty: &'static &'static str,
1047    range: &'static &'static str,
1048) -> Result<i128, Failed> {
1049    match value.as_i128() {
1050        Some(i) if i >= min && i <= max => Ok(i),
1051        Some(_) => Err(ctx.report_out_of_range(ty, range, value)),
1052        None => Err(ctx.report_expected_but_found(&"an integer", value)),
1053    }
1054}
1055
1056macro_rules! integer_new {
1057    ($($num:ty => $range:literal),+) => {$(
1058        impl<'de> FromToml<'de> for $num {
1059            fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1060                match deser_integer_ctx(ctx, value, <$num>::MIN as i128, <$num>::MAX as i128, &stringify!($num), &$range) {
1061                    Ok(i) => Ok(i as $num),
1062                    Err(e) => Err(e),
1063                }
1064            }
1065        }
1066    )+};
1067}
1068
1069integer_new!(
1070    i8 => "-128..=127",
1071    i16 => "-32768..=32767",
1072    i32 => "-2147483648..=2147483647",
1073    u8 => "0..=255",
1074    u16 => "0..=65535",
1075    u32 => "0..=4294967295"
1076);
1077
1078impl<'de> FromToml<'de> for isize {
1079    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1080        #[cfg(target_pointer_width = "32")]
1081        const RANGE: &str = "-2147483648..=2147483647";
1082        #[cfg(target_pointer_width = "64")]
1083        const RANGE: &str = "-9223372036854775808..=9223372036854775807";
1084        match deser_integer_ctx(
1085            ctx,
1086            value,
1087            isize::MIN as i128,
1088            isize::MAX as i128,
1089            &"isize",
1090            &RANGE,
1091        ) {
1092            Ok(i) => Ok(i as isize),
1093            Err(e) => Err(e),
1094        }
1095    }
1096}
1097
1098impl<'de> FromToml<'de> for i64 {
1099    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1100        match deser_integer_ctx(
1101            ctx,
1102            value,
1103            i64::MIN as i128,
1104            i64::MAX as i128,
1105            &"i64",
1106            &"-9223372036854775808..=9223372036854775807",
1107        ) {
1108            Ok(i) => Ok(i as i64),
1109            Err(e) => Err(e),
1110        }
1111    }
1112}
1113
1114impl<'de> FromToml<'de> for u64 {
1115    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1116        match deser_integer_ctx(
1117            ctx,
1118            value,
1119            0,
1120            u64::MAX as i128,
1121            &"u64",
1122            &"0..=18446744073709551615",
1123        ) {
1124            Ok(i) => Ok(i as u64),
1125            Err(e) => Err(e),
1126        }
1127    }
1128}
1129
1130impl<'de> FromToml<'de> for i128 {
1131    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1132        match deser_integer_ctx(
1133            ctx,
1134            value,
1135            i128::MIN,
1136            i128::MAX,
1137            &"i128",
1138            &"-170141183460469231731687303715884105728..=170141183460469231731687303715884105727",
1139        ) {
1140            Ok(i) => Ok(i),
1141            Err(e) => Err(e),
1142        }
1143    }
1144}
1145
1146impl<'de> FromToml<'de> for u128 {
1147    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1148        match deser_integer_ctx(
1149            ctx,
1150            value,
1151            0,
1152            i128::MAX,
1153            &"u128",
1154            &"0..=340282366920938463463374607431768211455",
1155        ) {
1156            Ok(i) => Ok(i as u128),
1157            Err(e) => Err(e),
1158        }
1159    }
1160}
1161
1162impl<'de> FromToml<'de> for usize {
1163    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1164        #[cfg(target_pointer_width = "32")]
1165        const RANGE: &str = "0..=4294967295";
1166        #[cfg(target_pointer_width = "64")]
1167        const RANGE: &str = "0..=18446744073709551615";
1168        match deser_integer_ctx(ctx, value, 0, usize::MAX as i128, &"usize", &RANGE) {
1169            Ok(i) => Ok(i as usize),
1170            Err(e) => Err(e),
1171        }
1172    }
1173}
1174
1175impl<'de> FromToml<'de> for f32 {
1176    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1177        match value.as_f64() {
1178            Some(f) => Ok(f as f32),
1179            None => Err(ctx.report_expected_but_found(&"a float", value)),
1180        }
1181    }
1182}
1183
1184impl<'de> FromToml<'de> for f64 {
1185    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1186        match value.as_f64() {
1187            Some(f) => Ok(f),
1188            None => Err(ctx.report_expected_but_found(&"a float", value)),
1189        }
1190    }
1191}
1192
1193impl<'de, T> FromToml<'de> for Vec<T>
1194where
1195    T: FromToml<'de>,
1196{
1197    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1198        let arr = value.require_array(ctx)?;
1199        let mut result = Vec::with_capacity(arr.len());
1200        let mut had_error = false;
1201        for item in arr {
1202            match T::from_toml(ctx, item) {
1203                Ok(v) => result.push(v),
1204                Err(_) => had_error = true,
1205            }
1206        }
1207        if had_error { Err(Failed) } else { Ok(result) }
1208    }
1209}
1210
1211impl<'de, T> FromToml<'de> for BTreeSet<T>
1212where
1213    T: Ord + FromToml<'de>,
1214{
1215    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1216        let arr = value.require_array(ctx)?;
1217        let mut result = BTreeSet::new();
1218        let mut had_error = false;
1219        for item in arr {
1220            match T::from_toml(ctx, item) {
1221                Ok(v) => {
1222                    result.insert(v);
1223                }
1224                Err(_) => had_error = true,
1225            }
1226        }
1227        if had_error { Err(Failed) } else { Ok(result) }
1228    }
1229}
1230
1231impl<'de, K, V> FromToml<'de> for BTreeMap<K, V>
1232where
1233    K: Ord + FromToml<'de>,
1234    V: FromToml<'de>,
1235{
1236    fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1237        let table = value.require_table(ctx)?;
1238        let mut map = BTreeMap::new();
1239        let mut had_error = false;
1240        for (key, item) in table {
1241            let k = match key_from_toml(ctx, key) {
1242                Ok(k) => k,
1243                Err(_) => {
1244                    had_error = true;
1245                    continue;
1246                }
1247            };
1248            match V::from_toml(ctx, item) {
1249                Ok(v) => {
1250                    map.insert(k, v);
1251                }
1252                Err(_) => had_error = true,
1253            }
1254        }
1255        if had_error { Err(Failed) } else { Ok(map) }
1256    }
1257}
1258
1259impl<'de> Item<'de> {
1260    /// Returns a string, or records an error with a custom `expected` message.
1261    ///
1262    /// Use instead of [`require_string`](Self::require_string) when the
1263    /// expected value is more specific than "a string", for example
1264    /// `"an IPv4 address"` or `"a hex color"`.
1265    #[doc(alias = "expect_custom_string")]
1266    pub fn require_custom_string(
1267        &self,
1268        ctx: &mut Context<'de>,
1269        expected: &'static &'static str,
1270    ) -> Result<&'de str, Failed> {
1271        match self.value() {
1272            item::Value::String(s) => Ok(*s),
1273            _ => Err(ctx.report_expected_but_found(expected, self)),
1274        }
1275    }
1276    /// Returns a string, or records an error if this is not a string.
1277    #[doc(alias = "expect_string")]
1278    pub fn require_string(&self, ctx: &mut Context<'de>) -> Result<&'de str, Failed> {
1279        match self.value() {
1280            item::Value::String(s) => Ok(*s),
1281            _ => Err(ctx.report_expected_but_found(&"a string", self)),
1282        }
1283    }
1284
1285    /// Returns an array reference, or records an error if this is not an array.
1286    #[doc(alias = "expect_array")]
1287    pub fn require_array(&self, ctx: &mut Context<'de>) -> Result<&crate::Array<'de>, Failed> {
1288        match self.as_array() {
1289            Some(arr) => Ok(arr),
1290            None => Err(ctx.report_expected_but_found(&"an array", self)),
1291        }
1292    }
1293
1294    /// Returns a table reference, or records an error if this is not a table.
1295    #[doc(alias = "expect_table")]
1296    pub fn require_table(&self, ctx: &mut Context<'de>) -> Result<&crate::Table<'de>, Failed> {
1297        match self.as_table() {
1298            Some(table) => Ok(table),
1299            None => Err(ctx.report_expected_but_found(&"a table", self)),
1300        }
1301    }
1302
1303    /// Creates a [`TableHelper`] for this item, returning an error if it is
1304    /// not a table.
1305    ///
1306    /// Typical entry point for implementing [`FromToml`].
1307    pub fn table_helper<'ctx, 'item>(
1308        &'item self,
1309        ctx: &'ctx mut Context<'de>,
1310    ) -> Result<TableHelper<'ctx, 'item, 'de>, Failed> {
1311        let Some(table) = self.as_table() else {
1312            return Err(ctx.report_expected_but_found(&"a table", self));
1313        };
1314        Ok(TableHelper::new(ctx, table))
1315    }
1316}
1317
1318/// Collects errors encountered during parsing and conversion.
1319///
1320/// Returned by [`from_str`](crate::from_str) and [`Document::to`](crate::Document::to).
1321/// Contains one or more [`Error`] values, each with its own source span.
1322///
1323/// # Examples
1324///
1325/// ```
1326/// let result = toml_spanner::from_str::<std::collections::HashMap<String, String>>(
1327///     "bad toml {"
1328/// );
1329/// assert!(result.is_err());
1330/// let err = result.unwrap_err();
1331/// assert!(!err.errors.is_empty());
1332/// ```
1333#[derive(Debug)]
1334pub struct FromTomlError {
1335    /// The accumulated errors.
1336    pub errors: Vec<Error>,
1337}
1338
1339impl Display for FromTomlError {
1340    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1341        let Some(first) = self.errors.first() else {
1342            return f.write_str("deserialization failed");
1343        };
1344        Display::fmt(first, f)?;
1345        let remaining = self.errors.len() - 1;
1346        if remaining > 0 {
1347            write!(
1348                f,
1349                " (+{remaining} more error{})",
1350                if remaining == 1 { "" } else { "s" }
1351            )?;
1352        }
1353        Ok(())
1354    }
1355}
1356
1357impl std::error::Error for FromTomlError {}
1358
1359impl From<Error> for FromTomlError {
1360    fn from(error: Error) -> Self {
1361        Self {
1362            errors: vec![error],
1363        }
1364    }
1365}
1366
1367impl From<Vec<Error>> for FromTomlError {
1368    fn from(errors: Vec<Error>) -> Self {
1369        Self { errors }
1370    }
1371}
1372
1373impl IntoIterator for FromTomlError {
1374    type Item = Error;
1375    type IntoIter = std::vec::IntoIter<Error>;
1376    fn into_iter(self) -> Self::IntoIter {
1377        self.errors.into_iter()
1378    }
1379}
1380
1381impl<'a> IntoIterator for &'a FromTomlError {
1382    type Item = &'a Error;
1383    type IntoIter = std::slice::Iter<'a, Error>;
1384    fn into_iter(self) -> Self::IntoIter {
1385        self.errors.iter()
1386    }
1387}