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