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