1 use std::fmt;
2
3 use error::Error;
4
5 type DeriveInputShape = String;
6 type FieldName = String;
7 type MetaFormat = String;
8
9 #[derive(Debug)]
10 // Don't want to publicly commit to ErrorKind supporting equality yet, but
11 // not having it makes testing very difficult.
12 #[cfg_attr(test, derive(Clone, PartialEq, Eq))]
13 pub(in error) enum ErrorKind {
14 /// An arbitrary error message.
15 Custom(String),
16 DuplicateField(FieldName),
17 MissingField(FieldName),
18 UnsupportedShape(DeriveInputShape),
19 UnknownField(ErrorUnknownField),
20 UnexpectedFormat(MetaFormat),
21 UnexpectedType(String),
22 UnknownValue(String),
23 TooFewItems(usize),
24 TooManyItems(usize),
25 /// A set of errors.
26 Multiple(Vec<Error>),
27
28 // TODO make this variant take `!` so it can't exist
29 #[doc(hidden)]
30 __NonExhaustive,
31 }
32
33 impl ErrorKind {
description(&self) -> &str34 pub fn description(&self) -> &str {
35 use self::ErrorKind::*;
36
37 match *self {
38 Custom(ref s) => s,
39 DuplicateField(_) => "Duplicate field",
40 MissingField(_) => "Missing field",
41 UnknownField(_) => "Unexpected field",
42 UnsupportedShape(_) => "Unsupported shape",
43 UnexpectedFormat(_) => "Unexpected meta-item format",
44 UnexpectedType(_) => "Unexpected literal type",
45 UnknownValue(_) => "Unknown literal value",
46 TooFewItems(_) => "Too few items",
47 TooManyItems(_) => "Too many items",
48 Multiple(_) => "Multiple errors",
49 __NonExhaustive => unreachable!(),
50 }
51 }
52
53 /// Deeply counts the number of errors this item represents.
len(&self) -> usize54 pub fn len(&self) -> usize {
55 if let ErrorKind::Multiple(ref items) = *self {
56 items.iter().map(Error::len).sum()
57 } else {
58 1
59 }
60 }
61 }
62
63 impl fmt::Display for ErrorKind {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result64 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65 use self::ErrorKind::*;
66
67 match *self {
68 Custom(ref s) => s.fmt(f),
69 DuplicateField(ref field) => write!(f, "Duplicate field `{}`", field),
70 MissingField(ref field) => write!(f, "Missing field `{}`", field),
71 UnknownField(ref field) => field.fmt(f),
72 UnsupportedShape(ref shape) => write!(f, "Unsupported shape `{}`", shape),
73 UnexpectedFormat(ref format) => write!(f, "Unexpected meta-item format `{}`", format),
74 UnexpectedType(ref ty) => write!(f, "Unexpected literal type `{}`", ty),
75 UnknownValue(ref val) => write!(f, "Unknown literal value `{}`", val),
76 TooFewItems(ref min) => write!(f, "Too few items: Expected at least {}", min),
77 TooManyItems(ref max) => write!(f, "Too many items: Expected no more than {}", max),
78 Multiple(ref items) if items.len() == 1 => items[0].fmt(f),
79 Multiple(ref items) => {
80 write!(f, "Multiple errors: (")?;
81 let mut first = true;
82 for item in items {
83 if !first {
84 write!(f, ", ")?;
85 } else {
86 first = false;
87 }
88
89 item.fmt(f)?;
90 }
91
92 write!(f, ")")
93 }
94 __NonExhaustive => unreachable!(),
95 }
96 }
97 }
98
99 impl From<ErrorUnknownField> for ErrorKind {
from(err: ErrorUnknownField) -> Self100 fn from(err: ErrorUnknownField) -> Self {
101 ErrorKind::UnknownField(err)
102 }
103 }
104
105 /// An error for an unknown field, with a possible "did-you-mean" suggestion to get
106 /// the user back on the right track.
107 #[derive(Debug)]
108 // Don't want to publicly commit to ErrorKind supporting equality yet, but
109 // not having it makes testing very difficult.
110 #[cfg_attr(test, derive(Clone, PartialEq, Eq))]
111 pub(in error) struct ErrorUnknownField {
112 name: String,
113 did_you_mean: Option<String>,
114 }
115
116 impl ErrorUnknownField {
new<I: Into<String>>(name: I, did_you_mean: Option<String>) -> Self117 pub fn new<I: Into<String>>(name: I, did_you_mean: Option<String>) -> Self {
118 ErrorUnknownField {
119 name: name.into(),
120 did_you_mean,
121 }
122 }
123
with_alts<'a, T, I>(field: &str, alternates: I) -> Self where T: AsRef<str> + 'a, I: IntoIterator<Item = &'a T>,124 pub fn with_alts<'a, T, I>(field: &str, alternates: I) -> Self
125 where
126 T: AsRef<str> + 'a,
127 I: IntoIterator<Item = &'a T>,
128 {
129 ErrorUnknownField::new(field, did_you_mean(field, alternates))
130 }
131
132 #[cfg(feature = "diagnostics")]
to_diagnostic(self, span: Option<::proc_macro2::Span>) -> ::proc_macro::Diagnostic133 pub fn to_diagnostic(self, span: Option<::proc_macro2::Span>) -> ::proc_macro::Diagnostic {
134 let base = span
135 .unwrap_or_else(::proc_macro2::Span::call_site)
136 .unwrap()
137 .error(self.top_line());
138 match self.did_you_mean {
139 Some(alt_name) => base.help(format!("did you mean `{}`?", alt_name)),
140 None => base,
141 }
142 }
143
144 #[cfg(feature = "diagnostics")]
top_line(&self) -> String145 fn top_line(&self) -> String {
146 format!("Unknown field: `{}`", self.name)
147 }
148 }
149
150 impl From<String> for ErrorUnknownField {
from(name: String) -> Self151 fn from(name: String) -> Self {
152 ErrorUnknownField::new(name, None)
153 }
154 }
155
156 impl<'a> From<&'a str> for ErrorUnknownField {
from(name: &'a str) -> Self157 fn from(name: &'a str) -> Self {
158 ErrorUnknownField::new(name, None)
159 }
160 }
161
162 impl fmt::Display for ErrorUnknownField {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result163 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
164 write!(f, "Unknown field: `{}`", self.name)?;
165
166 if let Some(ref did_you_mean) = self.did_you_mean {
167 write!(f, ". Did you mean `{}`?", did_you_mean)?;
168 }
169
170 Ok(())
171 }
172 }
173
174 #[cfg(feature = "suggestions")]
did_you_mean<'a, T, I>(field: &str, alternates: I) -> Option<String> where T: AsRef<str> + 'a, I: IntoIterator<Item = &'a T>,175 fn did_you_mean<'a, T, I>(field: &str, alternates: I) -> Option<String>
176 where
177 T: AsRef<str> + 'a,
178 I: IntoIterator<Item = &'a T>,
179 {
180 let mut candidate: Option<(f64, &str)> = None;
181 for pv in alternates {
182 let confidence = ::strsim::jaro_winkler(field, pv.as_ref());
183 if confidence > 0.8 && (candidate.is_none() || (candidate.as_ref().unwrap().0 < confidence))
184 {
185 candidate = Some((confidence, pv.as_ref()));
186 }
187 }
188 match candidate {
189 None => None,
190 Some((_, candidate)) => Some(candidate.into()),
191 }
192 }
193
194 #[cfg(not(feature = "suggestions"))]
did_you_mean<'a, T, I>(_field: &str, _alternates: I) -> Option<String> where T: AsRef<str> + 'a, I: IntoIterator<Item = &'a T>,195 fn did_you_mean<'a, T, I>(_field: &str, _alternates: I) -> Option<String>
196 where
197 T: AsRef<str> + 'a,
198 I: IntoIterator<Item = &'a T>,
199 {
200 None
201 }
202