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 {
description(&self) -> &str37 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.
len(&self) -> usize57 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 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result67 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 {
from(err: ErrorUnknownField) -> Self113 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 {
new<I: Into<String>>(name: I, did_you_mean: Option<(f64, String)>) -> Self130 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
with_alts<'a, T, I>(field: &str, alternates: I) -> Self where T: AsRef<str> + 'a, I: IntoIterator<Item = &'a T>,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.
add_alts<'a, T, I>(&mut self, alternates: I) where T: AsRef<str> + 'a, I: IntoIterator<Item = &'a T>,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")]
into_diagnostic(self, span: Option<::proc_macro2::Span>) -> ::proc_macro::Diagnostic164 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")]
top_line(&self) -> String176 fn top_line(&self) -> String {
177 format!("Unknown field: `{}`", self.name)
178 }
179 }
180
181 impl From<String> for ErrorUnknownField {
from(name: String) -> Self182 fn from(name: String) -> Self {
183 ErrorUnknownField::new(name, None)
184 }
185 }
186
187 impl<'a> From<&'a str> for ErrorUnknownField {
from(name: &'a str) -> Self188 fn from(name: &'a str) -> Self {
189 ErrorUnknownField::new(name, None)
190 }
191 }
192
193 impl fmt::Display for ErrorUnknownField {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result194 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)) = self.did_you_mean {
198 write!(f, ". Did you mean `{}`?", did_you_mean)?;
199 }
200
201 Ok(())
202 }
203 }
204
205 #[cfg(feature = "suggestions")]
did_you_mean<'a, T, I>(field: &str, alternates: I) -> Option<(f64, String)> where T: AsRef<str> + 'a, I: IntoIterator<Item = &'a T>,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 in alternates {
213 let confidence = ::strsim::jaro_winkler(field, 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, candidate)| (score, candidate.into()))
220 }
221
222 #[cfg(not(feature = "suggestions"))]
did_you_mean<'a, T, I>(_field: &str, _alternates: I) -> Option<(f64, String)> where T: AsRef<str> + 'a, I: IntoIterator<Item = &'a T>,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