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")]
584 pub fn custom_at(message: impl ToString, item: &Item<'_>) -> Error {
585 Error {
586 kind: ErrorInner::Custom(message.to_string().into()),
587 span: item.span(),
588 path: MaybeTomlPath::uncomputed(item),
589 }
590 }
591
592 #[cfg(feature = "from-toml")]
594 pub(crate) fn custom_static(message: &'static str, span: Span) -> Error {
595 Error {
596 kind: ErrorInner::Static(ErrorKind::Custom(message)),
597 span,
598 path: MaybeTomlPath::empty(),
599 }
600 }
601
602 pub(crate) fn new(kind: ErrorKind<'static>, span: Span) -> Error {
604 Error {
605 kind: ErrorInner::Static(kind),
606 span,
607 path: MaybeTomlPath::empty(),
608 }
609 }
610
611 pub(crate) fn new_with_path(kind: ErrorKind<'static>, span: Span, path: MaybeTomlPath) -> Self {
613 Error {
614 kind: ErrorInner::Static(kind),
615 span,
616 path,
617 }
618 }
619}
620
621impl<'a> Debug for ErrorKind<'a> {
622 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
623 f.write_str(self.kind_name())
624 }
625}
626
627fn kind_message(kind: ErrorKind<'_>) -> String {
628 let mut out = String::new();
629 kind_message_inner(kind, &mut out);
630 out
631}
632
633#[inline(never)]
634fn s_push(out: &mut String, s: &str) {
635 out.push_str(s);
636}
637
638#[inline(never)]
639fn s_push_char(out: &mut String, c: char) {
640 out.push(c);
641}
642
643fn push_escape(out: &mut String, c: char) {
644 if c.is_control() {
645 for esc in c.escape_default() {
646 s_push_char(out, esc);
647 }
648 } else {
649 s_push_char(out, c);
650 }
651}
652
653fn push_u32(out: &mut String, mut n: u32) {
654 let mut buf = [0u8; 10];
655 let mut i = buf.len();
656 if n == 0 {
657 s_push_char(out, '0');
658 return;
659 }
660 while n > 0 {
661 i -= 1;
662 buf[i] = b'0' + (n % 10) as u8;
663 n /= 10;
664 }
665 s_push(out, unsafe { std::str::from_utf8_unchecked(&buf[i..]) });
667}
668
669fn kind_message_inner(kind: ErrorKind<'_>, out: &mut String) {
670 match kind {
671 ErrorKind::Custom(message) => s_push(out, message),
672 ErrorKind::UnexpectedEof => s_push(out, "unexpected eof encountered"),
673 ErrorKind::FileTooLarge => s_push(out, "file is too large (maximum 512 MiB)"),
674 ErrorKind::InvalidCharInString(c) => {
675 s_push(out, "invalid character in string: `");
676 push_escape(out, c);
677 s_push_char(out, '`');
678 }
679 ErrorKind::InvalidEscape(c) => {
680 s_push(out, "invalid escape character in string: `");
681 push_escape(out, c);
682 s_push_char(out, '`');
683 }
684 ErrorKind::InvalidHexEscape(c) => {
685 s_push(out, "invalid hex escape character in string: `");
686 push_escape(out, c);
687 s_push_char(out, '`');
688 }
689 ErrorKind::InvalidEscapeValue(c) => {
690 s_push(out, "invalid unicode escape value `");
691 push_unicode_escape(out, c);
692 s_push_char(out, '`');
693 }
694 ErrorKind::Unexpected(c) => {
695 s_push(out, "unexpected character found: `");
696 push_escape(out, c);
697 s_push_char(out, '`');
698 }
699 ErrorKind::UnterminatedString(delim) => {
700 if delim == '\'' {
701 s_push(out, "unterminated literal string, expected `'`");
702 } else {
703 s_push(out, "unterminated basic string, expected `\"`");
704 }
705 }
706 ErrorKind::Wanted { expected, found } => {
707 s_push(out, "expected ");
708 s_push(out, expected);
709 s_push(out, ", found ");
710 s_push(out, found);
711 }
712 ErrorKind::InvalidInteger(reason)
713 | ErrorKind::InvalidFloat(reason)
714 | ErrorKind::InvalidDateTime(reason) => {
715 let prefix = match kind {
716 ErrorKind::InvalidInteger(_) => "invalid integer",
717 ErrorKind::InvalidFloat(_) => "invalid float",
718 _ => "invalid datetime",
719 };
720 s_push(out, prefix);
721 if !reason.is_empty() {
722 s_push(out, ": ");
723 s_push(out, reason);
724 }
725 }
726 ErrorKind::OutOfRange { ty, .. } => {
727 s_push(out, "value out of range for ");
728 s_push(out, ty);
729 }
730 ErrorKind::DuplicateTable { .. } => s_push(out, "redefinition of table"),
731 ErrorKind::DuplicateKey { .. } => s_push(out, "duplicate key"),
732 ErrorKind::RedefineAsArray { .. } => s_push(out, "table redefined as array"),
733 ErrorKind::MultilineStringKey => {
734 s_push(out, "multiline strings are not allowed for key");
735 }
736 ErrorKind::DottedKeyInvalidType { .. } => {
737 s_push(out, "dotted key attempted to extend non-table type");
738 }
739 ErrorKind::UnexpectedKey { .. } => s_push(out, "unexpected key"),
740 ErrorKind::UnquotedString => {
741 s_push(out, "string values must be quoted, expected string literal");
742 }
743 ErrorKind::MissingField(field) => {
744 s_push(out, "missing required key '");
745 s_push(out, field);
746 s_push_char(out, '\'');
747 }
748 ErrorKind::DuplicateField { field, .. } => {
749 s_push(out, "duplicate key '");
750 s_push(out, field);
751 s_push_char(out, '\'');
752 }
753 ErrorKind::Deprecated { old, new, .. } => {
754 s_push(out, "key '");
755 s_push(out, old);
756 s_push(out, "' is deprecated, use '");
757 s_push(out, new);
758 s_push(out, "' instead");
759 }
760 ErrorKind::UnexpectedValue { expected } => {
761 s_push(out, "unexpected value, expected one of: ");
762 let mut first = true;
763 for val in expected {
764 if !first {
765 s_push(out, ", ");
766 }
767 first = false;
768 s_push(out, val);
769 }
770 }
771 ErrorKind::UnexpectedVariant { expected } => {
772 s_push(out, "unknown variant, expected one of: ");
773 let mut first = true;
774 for val in expected {
775 if !first {
776 s_push(out, ", ");
777 }
778 first = false;
779 s_push(out, val);
780 }
781 }
782 ErrorKind::MissingArrayComma => {
783 s_push(out, "missing comma between elements, expected `,` in array");
784 }
785 ErrorKind::UnclosedArray => s_push(out, "unclosed array, expected `]`"),
786 ErrorKind::MissingInlineTableComma => {
787 s_push(
788 out,
789 "missing comma between fields, expected `,` in inline table",
790 );
791 }
792 ErrorKind::UnclosedInlineTable => s_push(out, "unclosed inline table, expected `}`"),
793 }
794}
795
796impl Display for Error {
797 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
798 f.write_str(&kind_message(self.kind()))?;
799 if let Some(path) = self.path() {
800 let components: &[PathComponent<'_>] = path;
801 let display = match self.kind() {
802 ErrorKind::DuplicateField { .. } | ErrorKind::Deprecated { .. } => {
803 &components[..components.len().saturating_sub(1)]
804 }
805 _ => components,
806 };
807 if !display.is_empty() {
808 f.write_str(" at `")?;
809 let mut out = String::new();
810 push_toml_path(&mut out, display);
811 f.write_str(&out)?;
812 f.write_str("`")?;
813 }
814 }
815 Ok(())
816 }
817}
818
819impl Debug for Error {
820 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
821 let kind = self.kind();
822 f.debug_struct("Error")
823 .field("kind", &kind.kind_name())
824 .field("message", &kind_message(kind))
825 .field("span", &self.span().range())
826 .field("path", &self.path())
827 .finish()
828 }
829}
830
831impl std::error::Error for Error {}
832
833impl From<(ErrorKind<'static>, Span)> for Error {
834 fn from((kind, span): (ErrorKind<'static>, Span)) -> Self {
835 Self {
836 kind: ErrorInner::Static(kind),
837 span,
838 path: MaybeTomlPath::empty(),
839 }
840 }
841}
842
843fn push_toml_path(out: &mut String, path: &[PathComponent<'_>]) {
844 let mut first = true;
845 for component in path.iter() {
846 match component {
847 PathComponent::Key(key) => {
848 if !first {
849 s_push_char(out, '.');
850 }
851 first = false;
852 if is_bare_key(key.name) {
853 s_push(out, key.name);
854 } else {
855 s_push_char(out, '"');
856 for ch in key.name.chars() {
857 match ch {
858 '"' => s_push(out, "\\\""),
859 '\\' => s_push(out, "\\\\"),
860 '\n' => s_push(out, "\\n"),
861 '\r' => s_push(out, "\\r"),
862 '\t' => s_push(out, "\\t"),
863 c if c.is_control() => {
864 push_unicode_escape(out, c as u32);
865 }
866 c => s_push_char(out, c),
867 }
868 }
869 s_push_char(out, '"');
870 }
871 }
872 PathComponent::Index(idx) => {
873 s_push_char(out, '[');
874 push_u32(out, *idx as u32);
875 s_push_char(out, ']');
876 }
877 }
878 }
879}
880
881fn push_unicode_escape(out: &mut String, n: u32) {
882 s_push(out, "\\u");
883 let mut buf = [0u8; 8];
884 let digits = if n <= 0xFFFF { 4 } else { 6 };
885 for i in (0..digits).rev() {
886 let nibble = ((n >> (i * 4)) & 0xF) as u8;
887 buf[digits - 1 - i] = if nibble < 10 {
888 b'0' + nibble
889 } else {
890 b'A' + nibble - 10
891 };
892 }
893 s_push(out, unsafe {
895 std::str::from_utf8_unchecked(&buf[..digits])
896 });
897}
898
899impl Error {
900 pub fn message(&self, source: &str) -> String {
904 let mut out = String::new();
905 self.message_inner(source, &mut out);
906 out
907 }
908
909 pub fn message_with_path(&self, source: &str) -> String {
913 let mut out = String::new();
914 self.message_inner(source, &mut out);
915 self.append_path(&mut out);
916 out
917 }
918
919 fn append_path(&self, out: &mut String) {
920 if let Some(p) = self.path() {
921 let components: &[PathComponent<'_>] = p;
922 let display = match self.kind() {
923 ErrorKind::DuplicateKey { .. }
924 | ErrorKind::DuplicateTable { .. }
925 | ErrorKind::DuplicateField { .. }
926 | ErrorKind::Deprecated { .. } => &components[..components.len().saturating_sub(1)],
927 _ => components,
928 };
929 if !display.is_empty() {
930 s_push(out, " at `");
931 push_toml_path(out, display);
932 s_push_char(out, '`');
933 }
934 }
935 }
936
937 fn message_inner(&self, source: &str, out: &mut String) {
938 let span = self.span;
939 let kind = self.kind();
940 let path = self.path();
941
942 match kind {
943 ErrorKind::DuplicateKey { .. } => {
944 if let Some(name) = source.get(span.range()) {
945 s_push(out, "the key `");
946 s_push(out, name);
947 s_push(out, "` is defined multiple times in table");
948 } else {
949 kind_message_inner(kind, out);
950 }
951 }
952 ErrorKind::DuplicateTable { name, .. } => {
953 if let Some(table_name) = source.get(name.range()) {
954 s_push(out, "redefinition of table `");
955 s_push(out, table_name);
956 s_push_char(out, '`');
957 } else {
958 kind_message_inner(kind, out);
959 }
960 }
961 ErrorKind::UnexpectedKey { .. } if path.is_none() => {
962 if let Some(key_name) = source.get(span.range()) {
963 s_push(out, "unexpected key `");
964 s_push(out, key_name);
965 s_push_char(out, '`');
966 } else {
967 kind_message_inner(kind, out);
968 }
969 }
970 ErrorKind::DuplicateField { field, .. } => {
971 if let Some(key_name) = source.get(span.range()) {
972 if key_name == field {
973 s_push(out, "key '");
974 s_push(out, field);
975 s_push(out, "' defined multiple times in same table");
976 } else {
977 s_push(out, "both '");
978 s_push(out, key_name);
979 s_push(out, "' and '");
980 s_push(out, field);
981 s_push(out, "' are defined but resolve to the same field");
982 }
983 } else {
984 kind_message_inner(kind, out);
985 }
986 }
987 ErrorKind::UnexpectedVariant { .. } => {
988 if let Some(value) = source.get(span.range()) {
989 s_push(out, "unknown variant ");
990 match value.split_once('\n') {
991 Some((first, _)) => {
992 s_push(out, first);
993 s_push(out, "...");
994 }
995 None => s_push(out, value),
996 }
997 } else {
998 kind_message_inner(kind, out);
999 }
1000 }
1001 _ => kind_message_inner(kind, out),
1002 }
1003 }
1004
1005 pub fn primary_label(&self) -> Option<(Span, String)> {
1007 let mut out = String::new();
1008 self.primary_label_inner(&mut out);
1009 Some((self.span, out))
1010 }
1011
1012 fn primary_label_inner(&self, out: &mut String) {
1013 let kind = self.kind();
1014 match kind {
1015 ErrorKind::DuplicateKey { .. } => s_push(out, "duplicate key"),
1016 ErrorKind::DuplicateTable { .. } => s_push(out, "duplicate table"),
1017 ErrorKind::DottedKeyInvalidType { .. } => {
1018 s_push(out, "attempted to extend table here");
1019 }
1020 ErrorKind::Unexpected(c) => {
1021 s_push(out, "unexpected character '");
1022 push_escape(out, c);
1023 s_push_char(out, '\'');
1024 }
1025 ErrorKind::InvalidCharInString(c) => {
1026 s_push(out, "invalid character '");
1027 push_escape(out, c);
1028 s_push(out, "' in string");
1029 }
1030 ErrorKind::InvalidEscape(c) => {
1031 s_push(out, "invalid escape character '");
1032 push_escape(out, c);
1033 s_push(out, "' in string");
1034 }
1035 ErrorKind::InvalidEscapeValue(_) => s_push(out, "invalid unicode escape value"),
1036 ErrorKind::InvalidInteger(_)
1037 | ErrorKind::InvalidFloat(_)
1038 | ErrorKind::InvalidDateTime(_) => kind_message_inner(kind, out),
1039 ErrorKind::InvalidHexEscape(c) => {
1040 s_push(out, "invalid hex escape '");
1041 push_escape(out, c);
1042 s_push_char(out, '\'');
1043 }
1044 ErrorKind::Wanted { expected, .. } => {
1045 s_push(out, "expected ");
1046 s_push(out, expected);
1047 }
1048 ErrorKind::MultilineStringKey => s_push(out, "multiline keys are not allowed"),
1049 ErrorKind::UnterminatedString(delim) => {
1050 s_push(out, "expected `");
1051 s_push_char(out, delim);
1052 s_push_char(out, '`');
1053 }
1054 ErrorKind::UnquotedString => s_push(out, "string is not quoted"),
1055 ErrorKind::UnexpectedKey { .. } => s_push(out, "unexpected key"),
1056 ErrorKind::MissingField(field) => {
1057 s_push(out, "missing key '");
1058 s_push(out, field);
1059 s_push_char(out, '\'');
1060 }
1061 ErrorKind::DuplicateField { .. } => s_push(out, "duplicate key"),
1062 ErrorKind::Deprecated { .. } => s_push(out, "deprecated key"),
1063 ErrorKind::UnexpectedValue { .. } => s_push(out, "unexpected value"),
1064 ErrorKind::UnexpectedVariant { expected } => {
1065 s_push(out, "expected one of: ");
1066 let mut first = true;
1067 for val in expected {
1068 if !first {
1069 s_push(out, ", ");
1070 }
1071 first = false;
1072 s_push(out, val);
1073 }
1074 }
1075 ErrorKind::MissingArrayComma => s_push(out, "expected `,`"),
1076 ErrorKind::UnclosedArray => s_push(out, "expected `]`"),
1077 ErrorKind::MissingInlineTableComma => s_push(out, "expected `,`"),
1078 ErrorKind::UnclosedInlineTable => s_push(out, "expected `}`"),
1079 ErrorKind::OutOfRange { range, .. } => {
1080 if !range.is_empty() {
1081 s_push(out, "expected ");
1082 s_push(out, range);
1083 }
1084 }
1085 ErrorKind::UnexpectedEof
1086 | ErrorKind::RedefineAsArray { .. }
1087 | ErrorKind::FileTooLarge
1088 | ErrorKind::Custom(..) => {}
1089 }
1090 }
1091
1092 pub fn secondary_label(&self) -> Option<(Span, String)> {
1097 let (first, text) = match self.kind() {
1098 ErrorKind::DuplicateKey { first } => (first, "first key instance"),
1099 ErrorKind::DuplicateTable { first, .. } => (first, "first table instance"),
1100 ErrorKind::DottedKeyInvalidType { first } => (first, "non-table"),
1101 ErrorKind::RedefineAsArray { first } => (first, "first defined as table"),
1102 ErrorKind::DuplicateField { first, .. } => (first, "first defined here"),
1103 _ => return None,
1104 };
1105 Some((first, String::from(text)))
1106 }
1107}