1#![allow(clippy::question_mark)]
2
3#[cfg(feature = "from-toml")]
4use crate::Item;
5use crate::{Key, Span};
6use std::fmt::{self, Debug, Display};
7
8#[derive(Clone, Copy)]
9pub enum PathComponent<'de> {
10 Key(Key<'de>),
11 Index(usize),
12}
13
14#[repr(transparent)]
46pub struct TomlPath<'a>([PathComponent<'a>]);
47
48impl<'a> std::ops::Deref for TomlPath<'a> {
49 type Target = [PathComponent<'a>];
50 fn deref(&self) -> &Self::Target {
51 &self.0
52 }
53}
54
55impl Debug for TomlPath<'_> {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 let mut out = String::new();
58 push_toml_path(&mut out, &self.0);
59 Debug::fmt(&out, f)
60 }
61}
62
63impl Display for TomlPath<'_> {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 let mut out = String::new();
66 push_toml_path(&mut out, &self.0);
67 f.write_str(&out)
68 }
69}
70
71fn is_bare_key(key: &str) -> bool {
72 if key.is_empty() {
73 return false;
74 }
75 for &b in key.as_bytes() {
76 match b {
77 b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'_' | b'-' => (),
78 _ => return false,
79 }
80 }
81 true
82}
83
84pub(crate) struct MaybeTomlPath {
85 ptr: std::ptr::NonNull<PathComponent<'static>>,
86 len: u32,
87 size: u32,
88}
89
90impl MaybeTomlPath {
91 pub(crate) fn empty() -> Self {
92 MaybeTomlPath {
93 ptr: std::ptr::NonNull::dangling(),
94 len: u32::MAX,
95 size: 0,
96 }
97 }
98
99 pub(crate) fn has_path(&self) -> bool {
100 self.size > 0
101 }
102
103 pub(crate) fn from_components(components: &[PathComponent<'_>]) -> MaybeTomlPath {
104 if components.is_empty() {
105 return Self::empty();
106 }
107
108 let len = components.len();
109 let mut total_string_bytes: usize = 0;
110 for comp in components {
111 if let PathComponent::Key(key) = comp {
112 total_string_bytes += key.name.len();
113 }
114 }
115
116 let comp_size = len * std::mem::size_of::<PathComponent<'static>>();
117 let size = comp_size + total_string_bytes;
118
119 let layout = std::alloc::Layout::from_size_align(
121 size,
122 std::mem::align_of::<PathComponent<'static>>(),
123 )
124 .unwrap();
125 let raw = unsafe { std::alloc::alloc(layout) };
127 if raw.is_null() {
128 std::alloc::handle_alloc_error(layout);
129 }
130
131 let base = raw.cast::<PathComponent<'static>>();
132 let mut string_cursor = unsafe { raw.add(comp_size) };
133
134 for (i, comp) in components.iter().enumerate() {
135 let stored = match comp {
136 PathComponent::Key(key) => {
137 let name_bytes = key.name.as_bytes();
138 let name_len = name_bytes.len();
139 unsafe {
141 std::ptr::copy_nonoverlapping(name_bytes.as_ptr(), string_cursor, name_len);
142 }
143 let name: &'static str = unsafe {
145 std::str::from_utf8_unchecked(std::slice::from_raw_parts(
146 string_cursor,
147 name_len,
148 ))
149 };
150 string_cursor = unsafe { string_cursor.add(name_len) };
151 PathComponent::Key(Key {
152 name,
153 span: key.span,
154 })
155 }
156 PathComponent::Index(idx) => PathComponent::Index(*idx),
157 };
158 unsafe {
160 base.add(i).write(stored);
161 }
162 }
163
164 MaybeTomlPath {
165 ptr: unsafe { std::ptr::NonNull::new_unchecked(base) },
167 len: len as u32,
168 size: size as u32,
169 }
170 }
171 #[cfg(feature = "from-toml")]
172 #[inline(always)]
173 pub(crate) fn uncomputed(item_ptr: *const Item<'_>) -> Self {
174 MaybeTomlPath {
175 ptr: unsafe {
178 std::ptr::NonNull::new_unchecked(item_ptr as *mut PathComponent<'static>)
179 },
180 len: 0,
181 size: 0,
182 }
183 }
184
185 #[cfg(feature = "from-toml")]
186 pub(crate) fn is_uncomputed(&self) -> bool {
187 self.size == 0 && self.len != u32::MAX
188 }
189
190 #[cfg(feature = "from-toml")]
191 pub(crate) fn uncomputed_ptr(&self) -> *const () {
192 self.ptr.as_ptr() as *const ()
193 }
194
195 fn as_toml_path<'a>(&'a self) -> Option<&'a TomlPath<'a>> {
196 if !self.has_path() {
197 return None;
198 }
199 let slice = unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len as usize) };
203 Some(unsafe { &*(slice as *const [PathComponent<'static>] as *const TomlPath<'a>) })
204 }
205}
206
207impl Drop for MaybeTomlPath {
208 fn drop(&mut self) {
209 let size = self.size as usize;
210 if size > 0 {
211 let layout = std::alloc::Layout::from_size_align(
212 size,
213 std::mem::align_of::<PathComponent<'static>>(),
214 )
215 .unwrap();
216 unsafe {
218 std::alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout);
219 }
220 }
221 }
222}
223
224unsafe impl Send for MaybeTomlPath {}
226unsafe impl Sync for MaybeTomlPath {}
228
229pub struct Error {
341 pub(crate) kind: ErrorInner,
342 pub(crate) span: Span,
343 pub(crate) path: MaybeTomlPath,
344}
345
346pub(crate) enum ErrorInner {
347 Static(ErrorKind<'static>),
348 Custom(Box<str>),
349}
350#[non_exhaustive]
352#[derive(Clone, Copy)]
353pub enum ErrorKind<'a> {
354 Custom(&'a str),
356
357 UnexpectedEof,
359
360 FileTooLarge,
362
363 InvalidCharInString(char),
365
366 InvalidEscape(char),
368
369 InvalidHexEscape(char),
371
372 InvalidEscapeValue(u32),
376
377 Unexpected(char),
380
381 UnterminatedString(char),
386
387 InvalidInteger(&'static str),
389
390 InvalidFloat(&'static str),
392
393 InvalidDateTime(&'static str),
395
396 OutOfRange {
399 ty: &'static &'static str,
401 range: &'static &'static str,
403 },
404
405 Wanted {
407 expected: &'static &'static str,
409 found: &'static &'static str,
411 },
412
413 DuplicateTable {
415 name: Span,
417 first: Span,
419 },
420
421 DuplicateKey {
423 first: Span,
425 },
426
427 RedefineAsArray {
429 first: Span,
431 },
432
433 MultilineStringKey,
435
436 DottedKeyInvalidType {
438 first: Span,
440 },
441
442 UnexpectedKey {
446 tag: u32,
449 },
450
451 UnquotedString,
453
454 MissingField(&'static str),
456
457 DuplicateField {
459 field: &'static str,
461 first: Span,
463 },
464
465 Deprecated {
467 tag: u32,
471 old: &'static &'static str,
473 new: &'static &'static str,
475 },
476
477 UnexpectedValue {
479 expected: &'static [&'static str],
481 },
482
483 UnexpectedVariant {
485 expected: &'static [&'static str],
487 },
488
489 MissingArrayComma,
491
492 UnclosedArray,
494
495 MissingInlineTableComma,
497
498 UnclosedInlineTable,
500}
501
502impl<'a> ErrorKind<'a> {
503 pub fn kind_name(&self) -> &'static str {
504 match self {
505 ErrorKind::Custom(_) => "Custom",
506 ErrorKind::UnexpectedEof => "UnexpectedEof",
507 ErrorKind::FileTooLarge => "FileTooLarge",
508 ErrorKind::InvalidCharInString(_) => "InvalidCharInString",
509 ErrorKind::InvalidEscape(_) => "InvalidEscape",
510 ErrorKind::InvalidHexEscape(_) => "InvalidHexEscape",
511 ErrorKind::InvalidEscapeValue(_) => "InvalidEscapeValue",
512 ErrorKind::Unexpected(_) => "Unexpected",
513 ErrorKind::UnterminatedString(_) => "UnterminatedString",
514 ErrorKind::InvalidInteger(_) => "InvalidInteger",
515 ErrorKind::InvalidFloat(_) => "InvalidFloat",
516 ErrorKind::InvalidDateTime(_) => "InvalidDateTime",
517 ErrorKind::OutOfRange { .. } => "OutOfRange",
518 ErrorKind::Wanted { .. } => "Wanted",
519 ErrorKind::DuplicateTable { .. } => "DuplicateTable",
520 ErrorKind::DuplicateKey { .. } => "DuplicateKey",
521 ErrorKind::RedefineAsArray { .. } => "RedefineAsArray",
522 ErrorKind::MultilineStringKey => "MultilineStringKey",
523 ErrorKind::DottedKeyInvalidType { .. } => "DottedKeyInvalidType",
524 ErrorKind::UnexpectedKey { .. } => "UnexpectedKey",
525 ErrorKind::UnquotedString => "UnquotedString",
526 ErrorKind::MissingField(_) => "MissingField",
527 ErrorKind::DuplicateField { .. } => "DuplicateField",
528 ErrorKind::Deprecated { .. } => "Deprecated",
529 ErrorKind::UnexpectedValue { .. } => "UnexpectedValue",
530 ErrorKind::UnexpectedVariant { .. } => "UnexpectedVariant",
531 ErrorKind::MissingArrayComma => "MissingArrayComma",
532 ErrorKind::UnclosedArray => "UnclosedArray",
533 ErrorKind::MissingInlineTableComma => "MissingInlineTableComma",
534 ErrorKind::UnclosedInlineTable => "UnclosedInlineTable",
535 }
536 }
537}
538
539impl Error {
540 pub fn span(&self) -> Span {
545 self.span
546 }
547
548 pub fn kind(&self) -> ErrorKind<'_> {
550 match &self.kind {
551 ErrorInner::Static(kind) => *kind,
552 ErrorInner::Custom(error) => ErrorKind::Custom(error),
553 }
554 }
555
556 pub fn path<'a>(&'a self) -> Option<&'a TomlPath<'a>> {
558 self.path.as_toml_path()
559 }
560
561 pub fn custom(message: impl ToString, span: Span) -> Error {
563 Error {
564 kind: ErrorInner::Custom(message.to_string().into()),
565 span,
566 path: MaybeTomlPath::empty(),
567 }
568 }
569
570 #[cfg(feature = "from-toml")]
572 pub(crate) fn custom_static(message: &'static str, span: Span) -> Error {
573 Error {
574 kind: ErrorInner::Static(ErrorKind::Custom(message)),
575 span,
576 path: MaybeTomlPath::empty(),
577 }
578 }
579
580 pub(crate) fn new(kind: ErrorKind<'static>, span: Span) -> Error {
582 Error {
583 kind: ErrorInner::Static(kind),
584 span,
585 path: MaybeTomlPath::empty(),
586 }
587 }
588
589 pub(crate) fn new_with_path(kind: ErrorKind<'static>, span: Span, path: MaybeTomlPath) -> Self {
591 Error {
592 kind: ErrorInner::Static(kind),
593 span,
594 path,
595 }
596 }
597}
598
599impl<'a> Debug for ErrorKind<'a> {
600 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
601 f.write_str(self.kind_name())
602 }
603}
604
605fn kind_message(kind: ErrorKind<'_>) -> String {
606 let mut out = String::new();
607 kind_message_inner(kind, &mut out);
608 out
609}
610
611#[inline(never)]
612fn s_push(out: &mut String, s: &str) {
613 out.push_str(s);
614}
615
616#[inline(never)]
617fn s_push_char(out: &mut String, c: char) {
618 out.push(c);
619}
620
621fn push_escape(out: &mut String, c: char) {
622 if c.is_control() {
623 for esc in c.escape_default() {
624 s_push_char(out, esc);
625 }
626 } else {
627 s_push_char(out, c);
628 }
629}
630
631fn push_u32(out: &mut String, mut n: u32) {
632 let mut buf = [0u8; 10];
633 let mut i = buf.len();
634 if n == 0 {
635 s_push_char(out, '0');
636 return;
637 }
638 while n > 0 {
639 i -= 1;
640 buf[i] = b'0' + (n % 10) as u8;
641 n /= 10;
642 }
643 s_push(out, unsafe { std::str::from_utf8_unchecked(&buf[i..]) });
645}
646
647fn kind_message_inner(kind: ErrorKind<'_>, out: &mut String) {
648 match kind {
649 ErrorKind::Custom(message) => s_push(out, message),
650 ErrorKind::UnexpectedEof => s_push(out, "unexpected eof encountered"),
651 ErrorKind::FileTooLarge => s_push(out, "file is too large (maximum 512 MiB)"),
652 ErrorKind::InvalidCharInString(c) => {
653 s_push(out, "invalid character in string: `");
654 push_escape(out, c);
655 s_push_char(out, '`');
656 }
657 ErrorKind::InvalidEscape(c) => {
658 s_push(out, "invalid escape character in string: `");
659 push_escape(out, c);
660 s_push_char(out, '`');
661 }
662 ErrorKind::InvalidHexEscape(c) => {
663 s_push(out, "invalid hex escape character in string: `");
664 push_escape(out, c);
665 s_push_char(out, '`');
666 }
667 ErrorKind::InvalidEscapeValue(c) => {
668 s_push(out, "invalid unicode escape value `");
669 push_unicode_escape(out, c);
670 s_push_char(out, '`');
671 }
672 ErrorKind::Unexpected(c) => {
673 s_push(out, "unexpected character found: `");
674 push_escape(out, c);
675 s_push_char(out, '`');
676 }
677 ErrorKind::UnterminatedString(delim) => {
678 if delim == '\'' {
679 s_push(out, "unterminated literal string, expected `'`");
680 } else {
681 s_push(out, "unterminated basic string, expected `\"`");
682 }
683 }
684 ErrorKind::Wanted { expected, found } => {
685 s_push(out, "expected ");
686 s_push(out, expected);
687 s_push(out, ", found ");
688 s_push(out, found);
689 }
690 ErrorKind::InvalidInteger(reason)
691 | ErrorKind::InvalidFloat(reason)
692 | ErrorKind::InvalidDateTime(reason) => {
693 let prefix = match kind {
694 ErrorKind::InvalidInteger(_) => "invalid integer",
695 ErrorKind::InvalidFloat(_) => "invalid float",
696 _ => "invalid datetime",
697 };
698 s_push(out, prefix);
699 if !reason.is_empty() {
700 s_push(out, ": ");
701 s_push(out, reason);
702 }
703 }
704 ErrorKind::OutOfRange { ty, .. } => {
705 s_push(out, "value out of range for ");
706 s_push(out, ty);
707 }
708 ErrorKind::DuplicateTable { .. } => s_push(out, "redefinition of table"),
709 ErrorKind::DuplicateKey { .. } => s_push(out, "duplicate key"),
710 ErrorKind::RedefineAsArray { .. } => s_push(out, "table redefined as array"),
711 ErrorKind::MultilineStringKey => {
712 s_push(out, "multiline strings are not allowed for key");
713 }
714 ErrorKind::DottedKeyInvalidType { .. } => {
715 s_push(out, "dotted key attempted to extend non-table type");
716 }
717 ErrorKind::UnexpectedKey { .. } => s_push(out, "unexpected key"),
718 ErrorKind::UnquotedString => {
719 s_push(out, "string values must be quoted, expected string literal");
720 }
721 ErrorKind::MissingField(field) => {
722 s_push(out, "missing required key '");
723 s_push(out, field);
724 s_push_char(out, '\'');
725 }
726 ErrorKind::DuplicateField { field, .. } => {
727 s_push(out, "duplicate key '");
728 s_push(out, field);
729 s_push_char(out, '\'');
730 }
731 ErrorKind::Deprecated { old, new, .. } => {
732 s_push(out, "key '");
733 s_push(out, old);
734 s_push(out, "' is deprecated, use '");
735 s_push(out, new);
736 s_push(out, "' instead");
737 }
738 ErrorKind::UnexpectedValue { expected } => {
739 s_push(out, "unexpected value, expected one of: ");
740 let mut first = true;
741 for val in expected {
742 if !first {
743 s_push(out, ", ");
744 }
745 first = false;
746 s_push(out, val);
747 }
748 }
749 ErrorKind::UnexpectedVariant { expected } => {
750 s_push(out, "unknown variant, expected one of: ");
751 let mut first = true;
752 for val in expected {
753 if !first {
754 s_push(out, ", ");
755 }
756 first = false;
757 s_push(out, val);
758 }
759 }
760 ErrorKind::MissingArrayComma => {
761 s_push(out, "missing comma between elements, expected `,` in array");
762 }
763 ErrorKind::UnclosedArray => s_push(out, "unclosed array, expected `]`"),
764 ErrorKind::MissingInlineTableComma => {
765 s_push(
766 out,
767 "missing comma between fields, expected `,` in inline table",
768 );
769 }
770 ErrorKind::UnclosedInlineTable => s_push(out, "unclosed inline table, expected `}`"),
771 }
772}
773
774impl Display for Error {
775 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
776 f.write_str(&kind_message(self.kind()))?;
777 if let Some(path) = self.path() {
778 let components: &[PathComponent<'_>] = path;
779 let display = match self.kind() {
780 ErrorKind::DuplicateField { .. } | ErrorKind::Deprecated { .. } => {
781 &components[..components.len().saturating_sub(1)]
782 }
783 _ => components,
784 };
785 if !display.is_empty() {
786 f.write_str(" at `")?;
787 let mut out = String::new();
788 push_toml_path(&mut out, display);
789 f.write_str(&out)?;
790 f.write_str("`")?;
791 }
792 }
793 Ok(())
794 }
795}
796
797impl Debug for Error {
798 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
799 let kind = self.kind();
800 f.debug_struct("Error")
801 .field("kind", &kind.kind_name())
802 .field("message", &kind_message(kind))
803 .field("span", &self.span().range())
804 .field("path", &self.path())
805 .finish()
806 }
807}
808
809impl std::error::Error for Error {}
810
811impl From<(ErrorKind<'static>, Span)> for Error {
812 fn from((kind, span): (ErrorKind<'static>, Span)) -> Self {
813 Self {
814 kind: ErrorInner::Static(kind),
815 span,
816 path: MaybeTomlPath::empty(),
817 }
818 }
819}
820
821fn push_toml_path(out: &mut String, path: &[PathComponent<'_>]) {
822 let mut first = true;
823 for component in path.iter() {
824 match component {
825 PathComponent::Key(key) => {
826 if !first {
827 s_push_char(out, '.');
828 }
829 first = false;
830 if is_bare_key(key.name) {
831 s_push(out, key.name);
832 } else {
833 s_push_char(out, '"');
834 for ch in key.name.chars() {
835 match ch {
836 '"' => s_push(out, "\\\""),
837 '\\' => s_push(out, "\\\\"),
838 '\n' => s_push(out, "\\n"),
839 '\r' => s_push(out, "\\r"),
840 '\t' => s_push(out, "\\t"),
841 c if c.is_control() => {
842 push_unicode_escape(out, c as u32);
843 }
844 c => s_push_char(out, c),
845 }
846 }
847 s_push_char(out, '"');
848 }
849 }
850 PathComponent::Index(idx) => {
851 s_push_char(out, '[');
852 push_u32(out, *idx as u32);
853 s_push_char(out, ']');
854 }
855 }
856 }
857}
858
859fn push_unicode_escape(out: &mut String, n: u32) {
860 s_push(out, "\\u");
861 let mut buf = [0u8; 8];
862 let digits = if n <= 0xFFFF { 4 } else { 6 };
863 for i in (0..digits).rev() {
864 let nibble = ((n >> (i * 4)) & 0xF) as u8;
865 buf[digits - 1 - i] = if nibble < 10 {
866 b'0' + nibble
867 } else {
868 b'A' + nibble - 10
869 };
870 }
871 s_push(out, unsafe {
873 std::str::from_utf8_unchecked(&buf[..digits])
874 });
875}
876
877impl Error {
878 pub fn message(&self, source: &str) -> String {
882 let mut out = String::new();
883 self.message_inner(source, &mut out);
884 out
885 }
886
887 pub fn message_with_path(&self, source: &str) -> String {
891 let mut out = String::new();
892 self.message_inner(source, &mut out);
893 self.append_path(&mut out);
894 out
895 }
896
897 fn append_path(&self, out: &mut String) {
898 if let Some(p) = self.path() {
899 let components: &[PathComponent<'_>] = p;
900 let display = match self.kind() {
901 ErrorKind::DuplicateKey { .. }
902 | ErrorKind::DuplicateTable { .. }
903 | ErrorKind::DuplicateField { .. }
904 | ErrorKind::Deprecated { .. } => &components[..components.len().saturating_sub(1)],
905 _ => components,
906 };
907 if !display.is_empty() {
908 s_push(out, " at `");
909 push_toml_path(out, display);
910 s_push_char(out, '`');
911 }
912 }
913 }
914
915 fn message_inner(&self, source: &str, out: &mut String) {
916 let span = self.span;
917 let kind = self.kind();
918 let path = self.path();
919
920 match kind {
921 ErrorKind::DuplicateKey { .. } => {
922 if let Some(name) = source.get(span.range()) {
923 s_push(out, "the key `");
924 s_push(out, name);
925 s_push(out, "` is defined multiple times in table");
926 } else {
927 kind_message_inner(kind, out);
928 }
929 }
930 ErrorKind::DuplicateTable { name, .. } => {
931 if let Some(table_name) = source.get(name.range()) {
932 s_push(out, "redefinition of table `");
933 s_push(out, table_name);
934 s_push_char(out, '`');
935 } else {
936 kind_message_inner(kind, out);
937 }
938 }
939 ErrorKind::UnexpectedKey { .. } if path.is_none() => {
940 if let Some(key_name) = source.get(span.range()) {
941 s_push(out, "unexpected key `");
942 s_push(out, key_name);
943 s_push_char(out, '`');
944 } else {
945 kind_message_inner(kind, out);
946 }
947 }
948 ErrorKind::DuplicateField { field, .. } => {
949 if let Some(key_name) = source.get(span.range()) {
950 if key_name == field {
951 s_push(out, "key '");
952 s_push(out, field);
953 s_push(out, "' defined multiple times in same table");
954 } else {
955 s_push(out, "both '");
956 s_push(out, key_name);
957 s_push(out, "' and '");
958 s_push(out, field);
959 s_push(out, "' are defined but resolve to the same field");
960 }
961 } else {
962 kind_message_inner(kind, out);
963 }
964 }
965 ErrorKind::UnexpectedVariant { .. } => {
966 if let Some(value) = source.get(span.range()) {
967 s_push(out, "unknown variant ");
968 match value.split_once('\n') {
969 Some((first, _)) => {
970 s_push(out, first);
971 s_push(out, "...");
972 }
973 None => s_push(out, value),
974 }
975 } else {
976 kind_message_inner(kind, out);
977 }
978 }
979 _ => kind_message_inner(kind, out),
980 }
981 }
982
983 pub fn primary_label(&self) -> Option<(Span, String)> {
985 let mut out = String::new();
986 self.primary_label_inner(&mut out);
987 Some((self.span, out))
988 }
989
990 fn primary_label_inner(&self, out: &mut String) {
991 let kind = self.kind();
992 match kind {
993 ErrorKind::DuplicateKey { .. } => s_push(out, "duplicate key"),
994 ErrorKind::DuplicateTable { .. } => s_push(out, "duplicate table"),
995 ErrorKind::DottedKeyInvalidType { .. } => {
996 s_push(out, "attempted to extend table here");
997 }
998 ErrorKind::Unexpected(c) => {
999 s_push(out, "unexpected character '");
1000 push_escape(out, c);
1001 s_push_char(out, '\'');
1002 }
1003 ErrorKind::InvalidCharInString(c) => {
1004 s_push(out, "invalid character '");
1005 push_escape(out, c);
1006 s_push(out, "' in string");
1007 }
1008 ErrorKind::InvalidEscape(c) => {
1009 s_push(out, "invalid escape character '");
1010 push_escape(out, c);
1011 s_push(out, "' in string");
1012 }
1013 ErrorKind::InvalidEscapeValue(_) => s_push(out, "invalid unicode escape value"),
1014 ErrorKind::InvalidInteger(_)
1015 | ErrorKind::InvalidFloat(_)
1016 | ErrorKind::InvalidDateTime(_) => kind_message_inner(kind, out),
1017 ErrorKind::InvalidHexEscape(c) => {
1018 s_push(out, "invalid hex escape '");
1019 push_escape(out, c);
1020 s_push_char(out, '\'');
1021 }
1022 ErrorKind::Wanted { expected, .. } => {
1023 s_push(out, "expected ");
1024 s_push(out, expected);
1025 }
1026 ErrorKind::MultilineStringKey => s_push(out, "multiline keys are not allowed"),
1027 ErrorKind::UnterminatedString(delim) => {
1028 s_push(out, "expected `");
1029 s_push_char(out, delim);
1030 s_push_char(out, '`');
1031 }
1032 ErrorKind::UnquotedString => s_push(out, "string is not quoted"),
1033 ErrorKind::UnexpectedKey { .. } => s_push(out, "unexpected key"),
1034 ErrorKind::MissingField(field) => {
1035 s_push(out, "missing key '");
1036 s_push(out, field);
1037 s_push_char(out, '\'');
1038 }
1039 ErrorKind::DuplicateField { .. } => s_push(out, "duplicate key"),
1040 ErrorKind::Deprecated { .. } => s_push(out, "deprecated key"),
1041 ErrorKind::UnexpectedValue { .. } => s_push(out, "unexpected value"),
1042 ErrorKind::UnexpectedVariant { expected } => {
1043 s_push(out, "expected one of: ");
1044 let mut first = true;
1045 for val in expected {
1046 if !first {
1047 s_push(out, ", ");
1048 }
1049 first = false;
1050 s_push(out, val);
1051 }
1052 }
1053 ErrorKind::MissingArrayComma => s_push(out, "expected `,`"),
1054 ErrorKind::UnclosedArray => s_push(out, "expected `]`"),
1055 ErrorKind::MissingInlineTableComma => s_push(out, "expected `,`"),
1056 ErrorKind::UnclosedInlineTable => s_push(out, "expected `}`"),
1057 ErrorKind::OutOfRange { range, .. } => {
1058 if !range.is_empty() {
1059 s_push(out, "expected ");
1060 s_push(out, range);
1061 }
1062 }
1063 ErrorKind::UnexpectedEof
1064 | ErrorKind::RedefineAsArray { .. }
1065 | ErrorKind::FileTooLarge
1066 | ErrorKind::Custom(..) => {}
1067 }
1068 }
1069
1070 pub fn secondary_label(&self) -> Option<(Span, String)> {
1075 let (first, text) = match self.kind() {
1076 ErrorKind::DuplicateKey { first } => (first, "first key instance"),
1077 ErrorKind::DuplicateTable { first, .. } => (first, "first table instance"),
1078 ErrorKind::DottedKeyInvalidType { first } => (first, "non-table"),
1079 ErrorKind::RedefineAsArray { first } => (first, "first defined as table"),
1080 ErrorKind::DuplicateField { first, .. } => (first, "first defined here"),
1081 _ => return None,
1082 };
1083 Some((first, String::from(text)))
1084 }
1085}