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
24pub(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 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 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 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 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
123pub struct TableHelper<'ctx, 'table, 'de> {
158 pub ctx: &'ctx mut Context<'de>,
159 pub table: &'table Table<'de>,
160 table_id: i32,
162 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 unsafe {
181 bitset.add(offset).write(0);
182 }
183 }
184 let slice = unsafe { std::slice::from_raw_parts_mut(bitset.as_ptr(), bitset_bucket_count) };
186 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
205pub 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 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 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 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 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 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 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 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 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 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 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 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 pub fn remaining_count(&self) -> usize {
461 self.table.len() - self.used_count as usize
462 }
463
464 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 #[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
514pub 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 pub fn source(&self) -> &'de str {
532 self.source
533 }
534
535 #[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 #[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 #[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 #[cold]
578 pub fn push_error(&mut self, error: Error) -> Failed {
579 self.errors.push(error);
580 Failed
581 }
582
583 #[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 #[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 #[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 #[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 #[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 #[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
678pub trait FromToml<'de>: Sized {
705 fn from_toml(ctx: &mut Context<'de>, item: &Item<'de>) -> Result<Self, Failed>;
707}
708
709#[diagnostic::on_unimplemented(
718 message = "`{Self}` does not implement `FromFlattened`",
719 note = "if `{Self}` implements `FromToml`, you can use `#[toml(flatten, with = flatten_any)]` instead of a manual `FromFlattened` impl"
720)]
721pub trait FromFlattened<'de>: Sized {
722 type Partial;
724 fn init() -> Self::Partial;
726 fn insert(
728 ctx: &mut Context<'de>,
729 key: &Key<'de>,
730 item: &Item<'de>,
731 partial: &mut Self::Partial,
732 ) -> Result<(), Failed>;
733 fn finish(
737 ctx: &mut Context<'de>,
738 parent: &Table<'de>,
739 partial: Self::Partial,
740 ) -> Result<Self, Failed>;
741}
742
743fn key_from_toml<'de, K: FromToml<'de>>(
745 ctx: &mut Context<'de>,
746 key: &Key<'de>,
747) -> Result<K, Failed> {
748 let item = Item::string_spanned(key.name, key.span);
749 K::from_toml(ctx, &item)
750}
751
752impl<'de, K, V, H> FromFlattened<'de> for std::collections::HashMap<K, V, H>
753where
754 K: Hash + Eq + FromToml<'de>,
755 V: FromToml<'de>,
756 H: Default + BuildHasher,
757{
758 type Partial = Self;
759 fn init() -> Self {
760 std::collections::HashMap::default()
761 }
762 fn insert(
763 ctx: &mut Context<'de>,
764 key: &Key<'de>,
765 item: &Item<'de>,
766 partial: &mut Self::Partial,
767 ) -> Result<(), Failed> {
768 let k = key_from_toml(ctx, key)?;
769 let v = match V::from_toml(ctx, item) {
770 Ok(v) => v,
771 Err(_) => return Err(Failed),
772 };
773 partial.insert(k, v);
774 Ok(())
775 }
776 fn finish(
777 _ctx: &mut Context<'de>,
778 _parent: &Table<'de>,
779 partial: Self::Partial,
780 ) -> Result<Self, Failed> {
781 Ok(partial)
782 }
783}
784
785impl<'de, K, V, H> FromToml<'de> for std::collections::HashMap<K, V, H>
786where
787 K: Hash + Eq + FromToml<'de>,
788 V: FromToml<'de>,
789 H: Default + BuildHasher,
790{
791 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
792 let table = value.require_table(ctx)?;
793 let mut map = std::collections::HashMap::default();
794 let mut had_error = false;
795 for (key, item) in table {
796 let k = match key_from_toml(ctx, key) {
797 Ok(k) => k,
798 Err(_) => {
799 had_error = true;
800 continue;
801 }
802 };
803 match V::from_toml(ctx, item) {
804 Ok(v) => {
805 map.insert(k, v);
806 }
807 Err(_) => had_error = true,
808 }
809 }
810 if had_error { Err(Failed) } else { Ok(map) }
811 }
812}
813
814impl<'de, K, V> FromFlattened<'de> for BTreeMap<K, V>
815where
816 K: Ord + FromToml<'de>,
817 V: FromToml<'de>,
818{
819 type Partial = Self;
820 fn init() -> Self {
821 BTreeMap::new()
822 }
823 fn insert(
824 ctx: &mut Context<'de>,
825 key: &Key<'de>,
826 item: &Item<'de>,
827 partial: &mut Self::Partial,
828 ) -> Result<(), Failed> {
829 let k = key_from_toml(ctx, key)?;
830 let v = match V::from_toml(ctx, item) {
831 Ok(v) => v,
832 Err(_) => return Err(Failed),
833 };
834 partial.insert(k, v);
835 Ok(())
836 }
837 fn finish(
838 _ctx: &mut Context<'de>,
839 _parent: &Table<'de>,
840 partial: Self::Partial,
841 ) -> Result<Self, Failed> {
842 Ok(partial)
843 }
844}
845
846impl<'de, T: FromToml<'de>, const N: usize> FromToml<'de> for [T; N] {
847 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
848 let boxed_slice = Box::<[T]>::from_toml(ctx, value)?;
849 match <Box<[T; N]>>::try_from(boxed_slice) {
850 Ok(array) => Ok(*array),
851 Err(res) => Err(ctx.push_error(Error::custom(
852 format!(
853 "expected an array with a size of {}, found one with a size of {}",
854 N,
855 res.len()
856 ),
857 value.span_unchecked(),
858 ))),
859 }
860 }
861}
862
863macro_rules! impl_from_toml_tuple {
864 ($len:expr, $($idx:tt => $T:ident, $var:ident),+) => {
865 impl<'de, $($T: FromToml<'de>),+> FromToml<'de> for ($($T,)+) {
866 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
867 let arr = value.require_array(ctx)?;
868 if arr.len() != $len {
869 return Err(ctx.push_error(Error::custom(
870 format!(
871 "expected an array with a size of {}, found one with a size of {}",
872 $len,
873 arr.len()
874 ),
875 value.span_unchecked(),
876 )));
877 }
878 let slice = arr.as_slice();
879 let mut had_error = false;
880 $(
881 let $var = match $T::from_toml(ctx, &slice[$idx]) {
882 Ok(v) => Some(v),
883 Err(_) => { had_error = true; None }
884 };
885 )+
886 if had_error {
887 return Err(Failed);
888 }
889 Ok(($($var.unwrap(),)+))
890 }
891 }
892 };
893}
894
895impl_from_toml_tuple!(1, 0 => A, a);
896impl_from_toml_tuple!(2, 0 => A, a, 1 => B, b);
897impl_from_toml_tuple!(3, 0 => A, a, 1 => B, b, 2 => C, c);
898
899impl<'de> FromToml<'de> for String {
900 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
901 match value.as_str() {
902 Some(s) => Ok(s.to_string()),
903 None => Err(ctx.report_expected_but_found(&"a string", value)),
904 }
905 }
906}
907
908impl<'de> FromToml<'de> for PathBuf {
909 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
910 match value.as_str() {
911 Some(s) => Ok(PathBuf::from(s)),
912 None => Err(ctx.report_expected_but_found(&"a path", value)),
913 }
914 }
915}
916
917impl<'de, T: FromToml<'de>> FromToml<'de> for Option<T> {
918 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
919 T::from_toml(ctx, value).map(Some)
920 }
921}
922
923impl<'de, T: FromToml<'de>> FromToml<'de> for Box<T> {
924 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
925 match T::from_toml(ctx, value) {
926 Ok(v) => Ok(Box::new(v)),
927 Err(e) => Err(e),
928 }
929 }
930}
931impl<'de, T: FromToml<'de>> FromToml<'de> for Box<[T]> {
932 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
933 match Vec::<T>::from_toml(ctx, value) {
934 Ok(vec) => Ok(vec.into_boxed_slice()),
935 Err(e) => Err(e),
936 }
937 }
938}
939impl<'de> FromToml<'de> for Box<str> {
940 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
941 match value.value() {
942 item::Value::String(&s) => Ok(s.into()),
943 _ => Err(ctx.report_expected_but_found(&"a string", value)),
944 }
945 }
946}
947impl<'de> FromToml<'de> for &'de str {
948 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
949 match value.value() {
950 item::Value::String(s) => Ok(*s),
951 _ => Err(ctx.report_expected_but_found(&"a string", value)),
952 }
953 }
954}
955
956impl<'de> FromToml<'de> for std::borrow::Cow<'de, str> {
957 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
958 match value.value() {
959 item::Value::String(s) => Ok(std::borrow::Cow::Borrowed(*s)),
960 _ => Err(ctx.report_expected_but_found(&"a string", value)),
961 }
962 }
963}
964
965impl<'de> FromToml<'de> for bool {
966 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
967 match value.as_bool() {
968 Some(b) => Ok(b),
969 None => Err(ctx.report_expected_but_found(&"a bool", value)),
970 }
971 }
972}
973
974fn deser_integer_ctx<'de>(
975 ctx: &mut Context<'de>,
976 value: &Item<'de>,
977 min: i128,
978 max: i128,
979 ty: &'static &'static str,
980 range: &'static &'static str,
981) -> Result<i128, Failed> {
982 match value.as_i128() {
983 Some(i) if i >= min && i <= max => Ok(i),
984 Some(_) => Err(ctx.report_out_of_range(ty, range, value)),
985 None => Err(ctx.report_expected_but_found(&"an integer", value)),
986 }
987}
988
989macro_rules! integer_new {
990 ($($num:ty => $range:literal),+) => {$(
991 impl<'de> FromToml<'de> for $num {
992 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
993 match deser_integer_ctx(ctx, value, <$num>::MIN as i128, <$num>::MAX as i128, &stringify!($num), &$range) {
994 Ok(i) => Ok(i as $num),
995 Err(e) => Err(e),
996 }
997 }
998 }
999 )+};
1000}
1001
1002integer_new!(
1003 i8 => "-128..=127",
1004 i16 => "-32768..=32767",
1005 i32 => "-2147483648..=2147483647",
1006 u8 => "0..=255",
1007 u16 => "0..=65535",
1008 u32 => "0..=4294967295"
1009);
1010
1011impl<'de> FromToml<'de> for isize {
1012 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1013 #[cfg(target_pointer_width = "32")]
1014 const RANGE: &str = "-2147483648..=2147483647";
1015 #[cfg(target_pointer_width = "64")]
1016 const RANGE: &str = "-9223372036854775808..=9223372036854775807";
1017 match deser_integer_ctx(
1018 ctx,
1019 value,
1020 isize::MIN as i128,
1021 isize::MAX as i128,
1022 &"isize",
1023 &RANGE,
1024 ) {
1025 Ok(i) => Ok(i as isize),
1026 Err(e) => Err(e),
1027 }
1028 }
1029}
1030
1031impl<'de> FromToml<'de> for i64 {
1032 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1033 match deser_integer_ctx(
1034 ctx,
1035 value,
1036 i64::MIN as i128,
1037 i64::MAX as i128,
1038 &"i64",
1039 &"-9223372036854775808..=9223372036854775807",
1040 ) {
1041 Ok(i) => Ok(i as i64),
1042 Err(e) => Err(e),
1043 }
1044 }
1045}
1046
1047impl<'de> FromToml<'de> for u64 {
1048 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1049 match deser_integer_ctx(
1050 ctx,
1051 value,
1052 0,
1053 u64::MAX as i128,
1054 &"u64",
1055 &"0..=18446744073709551615",
1056 ) {
1057 Ok(i) => Ok(i as u64),
1058 Err(e) => Err(e),
1059 }
1060 }
1061}
1062
1063impl<'de> FromToml<'de> for i128 {
1064 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1065 match deser_integer_ctx(
1066 ctx,
1067 value,
1068 i128::MIN,
1069 i128::MAX,
1070 &"i128",
1071 &"-170141183460469231731687303715884105728..=170141183460469231731687303715884105727",
1072 ) {
1073 Ok(i) => Ok(i),
1074 Err(e) => Err(e),
1075 }
1076 }
1077}
1078
1079impl<'de> FromToml<'de> for u128 {
1080 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1081 match deser_integer_ctx(
1082 ctx,
1083 value,
1084 0,
1085 i128::MAX,
1086 &"u128",
1087 &"0..=340282366920938463463374607431768211455",
1088 ) {
1089 Ok(i) => Ok(i as u128),
1090 Err(e) => Err(e),
1091 }
1092 }
1093}
1094
1095impl<'de> FromToml<'de> for usize {
1096 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1097 #[cfg(target_pointer_width = "32")]
1098 const RANGE: &str = "0..=4294967295";
1099 #[cfg(target_pointer_width = "64")]
1100 const RANGE: &str = "0..=18446744073709551615";
1101 match deser_integer_ctx(ctx, value, 0, usize::MAX as i128, &"usize", &RANGE) {
1102 Ok(i) => Ok(i as usize),
1103 Err(e) => Err(e),
1104 }
1105 }
1106}
1107
1108impl<'de> FromToml<'de> for f32 {
1109 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1110 match value.as_f64() {
1111 Some(f) => Ok(f as f32),
1112 None => Err(ctx.report_expected_but_found(&"a float", value)),
1113 }
1114 }
1115}
1116
1117impl<'de> FromToml<'de> for f64 {
1118 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1119 match value.as_f64() {
1120 Some(f) => Ok(f),
1121 None => Err(ctx.report_expected_but_found(&"a float", value)),
1122 }
1123 }
1124}
1125
1126impl<'de, T> FromToml<'de> for Vec<T>
1127where
1128 T: FromToml<'de>,
1129{
1130 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1131 let arr = value.require_array(ctx)?;
1132 let mut result = Vec::with_capacity(arr.len());
1133 let mut had_error = false;
1134 for item in arr {
1135 match T::from_toml(ctx, item) {
1136 Ok(v) => result.push(v),
1137 Err(_) => had_error = true,
1138 }
1139 }
1140 if had_error { Err(Failed) } else { Ok(result) }
1141 }
1142}
1143
1144impl<'de, T> FromToml<'de> for BTreeSet<T>
1145where
1146 T: Ord + FromToml<'de>,
1147{
1148 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1149 let arr = value.require_array(ctx)?;
1150 let mut result = BTreeSet::new();
1151 let mut had_error = false;
1152 for item in arr {
1153 match T::from_toml(ctx, item) {
1154 Ok(v) => {
1155 result.insert(v);
1156 }
1157 Err(_) => had_error = true,
1158 }
1159 }
1160 if had_error { Err(Failed) } else { Ok(result) }
1161 }
1162}
1163
1164impl<'de, K, V> FromToml<'de> for BTreeMap<K, V>
1165where
1166 K: Ord + FromToml<'de>,
1167 V: FromToml<'de>,
1168{
1169 fn from_toml(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
1170 let table = value.require_table(ctx)?;
1171 let mut map = BTreeMap::new();
1172 let mut had_error = false;
1173 for (key, item) in table {
1174 let k = match key_from_toml(ctx, key) {
1175 Ok(k) => k,
1176 Err(_) => {
1177 had_error = true;
1178 continue;
1179 }
1180 };
1181 match V::from_toml(ctx, item) {
1182 Ok(v) => {
1183 map.insert(k, v);
1184 }
1185 Err(_) => had_error = true,
1186 }
1187 }
1188 if had_error { Err(Failed) } else { Ok(map) }
1189 }
1190}
1191
1192impl<'de> Item<'de> {
1193 #[doc(alias = "expect_custom_string")]
1199 pub fn require_custom_string(
1200 &self,
1201 ctx: &mut Context<'de>,
1202 expected: &'static &'static str,
1203 ) -> Result<&'de str, Failed> {
1204 match self.value() {
1205 item::Value::String(s) => Ok(*s),
1206 _ => Err(ctx.report_expected_but_found(expected, self)),
1207 }
1208 }
1209 #[doc(alias = "expect_string")]
1211 pub fn require_string(&self, ctx: &mut Context<'de>) -> Result<&'de str, Failed> {
1212 match self.value() {
1213 item::Value::String(s) => Ok(*s),
1214 _ => Err(ctx.report_expected_but_found(&"a string", self)),
1215 }
1216 }
1217
1218 #[doc(alias = "expect_array")]
1220 pub fn require_array(&self, ctx: &mut Context<'de>) -> Result<&crate::Array<'de>, Failed> {
1221 match self.as_array() {
1222 Some(arr) => Ok(arr),
1223 None => Err(ctx.report_expected_but_found(&"an array", self)),
1224 }
1225 }
1226
1227 #[doc(alias = "expect_table")]
1229 pub fn require_table(&self, ctx: &mut Context<'de>) -> Result<&crate::Table<'de>, Failed> {
1230 match self.as_table() {
1231 Some(table) => Ok(table),
1232 None => Err(ctx.report_expected_but_found(&"a table", self)),
1233 }
1234 }
1235
1236 pub fn table_helper<'ctx, 'item>(
1241 &'item self,
1242 ctx: &'ctx mut Context<'de>,
1243 ) -> Result<TableHelper<'ctx, 'item, 'de>, Failed> {
1244 let Some(table) = self.as_table() else {
1245 return Err(ctx.report_expected_but_found(&"a table", self));
1246 };
1247 Ok(TableHelper::new(ctx, table))
1248 }
1249}
1250
1251#[derive(Debug)]
1267pub struct FromTomlError {
1268 pub errors: Vec<Error>,
1270}
1271
1272impl Display for FromTomlError {
1273 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1274 let Some(first) = self.errors.first() else {
1275 return f.write_str("deserialization failed");
1276 };
1277 Display::fmt(first, f)?;
1278 let remaining = self.errors.len() - 1;
1279 if remaining > 0 {
1280 write!(
1281 f,
1282 " (+{remaining} more error{})",
1283 if remaining == 1 { "" } else { "s" }
1284 )?;
1285 }
1286 Ok(())
1287 }
1288}
1289
1290impl std::error::Error for FromTomlError {}
1291
1292impl From<Error> for FromTomlError {
1293 fn from(error: Error) -> Self {
1294 Self {
1295 errors: vec![error],
1296 }
1297 }
1298}
1299
1300impl From<Vec<Error>> for FromTomlError {
1301 fn from(errors: Vec<Error>) -> Self {
1302 Self { errors }
1303 }
1304}
1305
1306impl IntoIterator for FromTomlError {
1307 type Item = Error;
1308 type IntoIter = std::vec::IntoIter<Error>;
1309 fn into_iter(self) -> Self::IntoIter {
1310 self.errors.into_iter()
1311 }
1312}
1313
1314impl<'a> IntoIterator for &'a FromTomlError {
1315 type Item = &'a Error;
1316 type IntoIter = std::slice::Iter<'a, Error>;
1317 fn into_iter(self) -> Self::IntoIter {
1318 self.errors.iter()
1319 }
1320}