1 | use std::borrow::Cow; |
2 | use std::cell::RefCell; |
3 | use std::collections::hash_map::HashMap; |
4 | use std::collections::HashSet; |
5 | use std::hash::BuildHasher; |
6 | use std::rc::Rc; |
7 | use std::sync::atomic::AtomicBool; |
8 | use std::sync::Arc; |
9 | |
10 | use syn::{Expr, Lit, Meta, NestedMeta}; |
11 | |
12 | use crate::{util::path_to_string, Error, Result}; |
13 | |
14 | /// Create an instance from an item in an attribute declaration. |
15 | /// |
16 | /// # Implementing `FromMeta` |
17 | /// * Do not take a dependency on the `ident` of the passed-in meta item. The ident will be set by the field name of the containing struct. |
18 | /// * Implement only the `from_*` methods that you intend to support. The default implementations will return useful errors. |
19 | /// |
20 | /// # Provided Implementations |
21 | /// ## bool |
22 | /// |
23 | /// * Word with no value specified - becomes `true`. |
24 | /// * As a boolean literal, e.g. `foo = true`. |
25 | /// * As a string literal, e.g. `foo = "true"`. |
26 | /// |
27 | /// ## char |
28 | /// * As a char literal, e.g. `foo = '#'`. |
29 | /// * As a string literal consisting of a single character, e.g. `foo = "#"`. |
30 | /// |
31 | /// ## String |
32 | /// * As a string literal, e.g. `foo = "hello"`. |
33 | /// * As a raw string literal, e.g. `foo = r#"hello "world""#`. |
34 | /// |
35 | /// ## Number |
36 | /// * As a string literal, e.g. `foo = "-25"`. |
37 | /// * As an unquoted positive value, e.g. `foo = 404`. Negative numbers must be in quotation marks. |
38 | /// |
39 | /// ## () |
40 | /// * Word with no value specified, e.g. `foo`. This is best used with `Option`. |
41 | /// See `darling::util::Flag` for a more strongly-typed alternative. |
42 | /// |
43 | /// ## Option |
44 | /// * Any format produces `Some`. |
45 | /// |
46 | /// ## `Result<T, darling::Error>` |
47 | /// * Allows for fallible parsing; will populate the target field with the result of the |
48 | /// parse attempt. |
49 | pub trait FromMeta: Sized { |
50 | fn from_nested_meta(item: &NestedMeta) -> Result<Self> { |
51 | (match *item { |
52 | NestedMeta::Lit(ref lit) => Self::from_value(lit), |
53 | NestedMeta::Meta(ref mi) => Self::from_meta(mi), |
54 | }) |
55 | .map_err(|e| e.with_span(item)) |
56 | } |
57 | |
58 | /// Create an instance from a `syn::Meta` by dispatching to the format-appropriate |
59 | /// trait function. This generally should not be overridden by implementers. |
60 | /// |
61 | /// # Error Spans |
62 | /// If this method is overridden and can introduce errors that weren't passed up from |
63 | /// other `from_meta` calls, the override must call `with_span` on the error using the |
64 | /// `item` to make sure that the emitted diagnostic points to the correct location in |
65 | /// source code. |
66 | fn from_meta(item: &Meta) -> Result<Self> { |
67 | (match *item { |
68 | Meta::Path(_) => Self::from_word(), |
69 | Meta::List(ref value) => Self::from_list( |
70 | &value |
71 | .nested |
72 | .iter() |
73 | .cloned() |
74 | .collect::<Vec<syn::NestedMeta>>()[..], |
75 | ), |
76 | Meta::NameValue(ref value) => Self::from_value(&value.lit), |
77 | }) |
78 | .map_err(|e| e.with_span(item)) |
79 | } |
80 | |
81 | /// When a field is omitted from a parent meta-item, `from_none` is used to attempt |
82 | /// recovery before a missing field error is generated. |
83 | /// |
84 | /// **Most types should not override this method.** `darling` already allows field-level |
85 | /// missing-field recovery using `#[darling(default)]` and `#[darling(default = "...")]`, |
86 | /// and users who add a `String` field to their `FromMeta`-deriving struct would be surprised |
87 | /// if they get back `""` instead of a missing field error when that field is omitted. |
88 | /// |
89 | /// The primary use-case for this is `Option<T>` fields gracefully handlling absence without |
90 | /// needing `#[darling(default)]`. |
91 | fn from_none() -> Option<Self> { |
92 | None |
93 | } |
94 | |
95 | /// Create an instance from the presence of the word in the attribute with no |
96 | /// additional options specified. |
97 | fn from_word() -> Result<Self> { |
98 | Err(Error::unsupported_format("word" )) |
99 | } |
100 | |
101 | /// Create an instance from a list of nested meta items. |
102 | #[allow (unused_variables)] |
103 | fn from_list(items: &[NestedMeta]) -> Result<Self> { |
104 | Err(Error::unsupported_format("list" )) |
105 | } |
106 | |
107 | /// Create an instance from a literal value of either `foo = "bar"` or `foo("bar")`. |
108 | /// This dispatches to the appropriate method based on the type of literal encountered, |
109 | /// and generally should not be overridden by implementers. |
110 | /// |
111 | /// # Error Spans |
112 | /// If this method is overridden, the override must make sure to add `value`'s span |
113 | /// information to the returned error by calling `with_span(value)` on the `Error` instance. |
114 | fn from_value(value: &Lit) -> Result<Self> { |
115 | (match *value { |
116 | Lit::Bool(ref b) => Self::from_bool(b.value), |
117 | Lit::Str(ref s) => Self::from_string(&s.value()), |
118 | Lit::Char(ref ch) => Self::from_char(ch.value()), |
119 | _ => Err(Error::unexpected_lit_type(value)), |
120 | }) |
121 | .map_err(|e| e.with_span(value)) |
122 | } |
123 | |
124 | /// Create an instance from a char literal in a value position. |
125 | #[allow (unused_variables)] |
126 | fn from_char(value: char) -> Result<Self> { |
127 | Err(Error::unexpected_type("char" )) |
128 | } |
129 | |
130 | /// Create an instance from a string literal in a value position. |
131 | #[allow (unused_variables)] |
132 | fn from_string(value: &str) -> Result<Self> { |
133 | Err(Error::unexpected_type("string" )) |
134 | } |
135 | |
136 | /// Create an instance from a bool literal in a value position. |
137 | #[allow (unused_variables)] |
138 | fn from_bool(value: bool) -> Result<Self> { |
139 | Err(Error::unexpected_type("bool" )) |
140 | } |
141 | } |
142 | |
143 | // FromMeta impls for std and syn types. |
144 | |
145 | impl FromMeta for () { |
146 | fn from_word() -> Result<Self> { |
147 | Ok(()) |
148 | } |
149 | } |
150 | |
151 | impl FromMeta for bool { |
152 | fn from_word() -> Result<Self> { |
153 | Ok(true) |
154 | } |
155 | |
156 | #[allow (clippy::wrong_self_convention)] // false positive |
157 | fn from_bool(value: bool) -> Result<Self> { |
158 | Ok(value) |
159 | } |
160 | |
161 | fn from_string(value: &str) -> Result<Self> { |
162 | value.parse().map_err(|_| Error::unknown_value(value)) |
163 | } |
164 | } |
165 | |
166 | impl FromMeta for AtomicBool { |
167 | fn from_meta(mi: &Meta) -> Result<Self> { |
168 | FromMeta::from_meta(mi) |
169 | .map(AtomicBool::new) |
170 | .map_err(|e: Error| e.with_span(node:mi)) |
171 | } |
172 | } |
173 | |
174 | impl FromMeta for char { |
175 | #[allow (clippy::wrong_self_convention)] // false positive |
176 | fn from_char(value: char) -> Result<Self> { |
177 | Ok(value) |
178 | } |
179 | |
180 | fn from_string(s: &str) -> Result<Self> { |
181 | let mut chars: Chars<'_> = s.chars(); |
182 | let char1: Option = chars.next(); |
183 | let char2: Option = chars.next(); |
184 | |
185 | if let (Some(char: char), None) = (char1, char2) { |
186 | Ok(char) |
187 | } else { |
188 | Err(Error::unexpected_type(ty:"string" )) |
189 | } |
190 | } |
191 | } |
192 | |
193 | impl FromMeta for String { |
194 | fn from_string(s: &str) -> Result<Self> { |
195 | Ok(s.to_string()) |
196 | } |
197 | } |
198 | |
199 | /// Generate an impl of `FromMeta` that will accept strings which parse to numbers or |
200 | /// integer literals. |
201 | macro_rules! from_meta_num { |
202 | ($ty:ident) => { |
203 | impl FromMeta for $ty { |
204 | fn from_string(s: &str) -> Result<Self> { |
205 | s.parse().map_err(|_| Error::unknown_value(s)) |
206 | } |
207 | |
208 | fn from_value(value: &Lit) -> Result<Self> { |
209 | (match *value { |
210 | Lit::Str(ref s) => Self::from_string(&s.value()), |
211 | Lit::Int(ref s) => Ok(s.base10_parse::<$ty>().unwrap()), |
212 | _ => Err(Error::unexpected_lit_type(value)), |
213 | }) |
214 | .map_err(|e| e.with_span(value)) |
215 | } |
216 | } |
217 | }; |
218 | } |
219 | |
220 | from_meta_num!(u8); |
221 | from_meta_num!(u16); |
222 | from_meta_num!(u32); |
223 | from_meta_num!(u64); |
224 | from_meta_num!(usize); |
225 | from_meta_num!(i8); |
226 | from_meta_num!(i16); |
227 | from_meta_num!(i32); |
228 | from_meta_num!(i64); |
229 | from_meta_num!(isize); |
230 | |
231 | /// Generate an impl of `FromMeta` that will accept strings which parse to floats or |
232 | /// float literals. |
233 | macro_rules! from_meta_float { |
234 | ($ty:ident) => { |
235 | impl FromMeta for $ty { |
236 | fn from_string(s: &str) -> Result<Self> { |
237 | s.parse().map_err(|_| Error::unknown_value(s)) |
238 | } |
239 | |
240 | fn from_value(value: &Lit) -> Result<Self> { |
241 | (match *value { |
242 | Lit::Str(ref s) => Self::from_string(&s.value()), |
243 | Lit::Float(ref s) => Ok(s.base10_parse::<$ty>().unwrap()), |
244 | _ => Err(Error::unexpected_lit_type(value)), |
245 | }) |
246 | .map_err(|e| e.with_span(value)) |
247 | } |
248 | } |
249 | }; |
250 | } |
251 | |
252 | from_meta_float!(f32); |
253 | from_meta_float!(f64); |
254 | |
255 | /// Parsing support for punctuated. This attempts to preserve span information |
256 | /// when available, but also supports parsing strings with the call site as the |
257 | /// emitted span. |
258 | impl<T: syn::parse::Parse, P: syn::parse::Parse> FromMeta for syn::punctuated::Punctuated<T, P> { |
259 | fn from_value(value: &Lit) -> Result<Self> { |
260 | if let Lit::Str(ref ident: &LitStr) = *value { |
261 | ident |
262 | .parse_with(syn::punctuated::Punctuated::parse_terminated) |
263 | .map_err(|_| Error::unknown_lit_str_value(ident)) |
264 | } else { |
265 | Err(Error::unexpected_lit_type(lit:value)) |
266 | } |
267 | } |
268 | } |
269 | |
270 | /// Adapter from `syn::parse::Parse` to `FromMeta`. |
271 | /// |
272 | /// This cannot be a blanket impl, due to the `syn::Lit` family's need to handle non-string values. |
273 | /// Therefore, we use a macro and a lot of impls. |
274 | macro_rules! from_syn_parse { |
275 | ($ty:path) => { |
276 | impl FromMeta for $ty { |
277 | fn from_string(value: &str) -> Result<Self> { |
278 | syn::parse_str(value).map_err(|_| Error::unknown_value(value)) |
279 | } |
280 | |
281 | fn from_value(value: &::syn::Lit) -> Result<Self> { |
282 | if let ::syn::Lit::Str(ref v) = *value { |
283 | v.parse::<$ty>() |
284 | .map_err(|_| Error::unknown_lit_str_value(v)) |
285 | } else { |
286 | Err(Error::unexpected_lit_type(value)) |
287 | } |
288 | } |
289 | } |
290 | }; |
291 | } |
292 | |
293 | from_syn_parse!(syn::Ident); |
294 | from_syn_parse!(syn::Expr); |
295 | from_syn_parse!(syn::ExprArray); |
296 | from_syn_parse!(syn::ExprPath); |
297 | from_syn_parse!(syn::Path); |
298 | from_syn_parse!(syn::Type); |
299 | from_syn_parse!(syn::TypeArray); |
300 | from_syn_parse!(syn::TypeBareFn); |
301 | from_syn_parse!(syn::TypeGroup); |
302 | from_syn_parse!(syn::TypeImplTrait); |
303 | from_syn_parse!(syn::TypeInfer); |
304 | from_syn_parse!(syn::TypeMacro); |
305 | from_syn_parse!(syn::TypeNever); |
306 | from_syn_parse!(syn::TypeParam); |
307 | from_syn_parse!(syn::TypeParen); |
308 | from_syn_parse!(syn::TypePath); |
309 | from_syn_parse!(syn::TypePtr); |
310 | from_syn_parse!(syn::TypeReference); |
311 | from_syn_parse!(syn::TypeSlice); |
312 | from_syn_parse!(syn::TypeTraitObject); |
313 | from_syn_parse!(syn::TypeTuple); |
314 | from_syn_parse!(syn::Visibility); |
315 | from_syn_parse!(syn::WhereClause); |
316 | |
317 | macro_rules! from_numeric_array { |
318 | ($ty:ident) => { |
319 | /// Parsing an unsigned integer array, i.e. `example = "[1, 2, 3, 4]"`. |
320 | impl FromMeta for Vec<$ty> { |
321 | fn from_value(value: &Lit) -> Result<Self> { |
322 | let expr_array = syn::ExprArray::from_value(value)?; |
323 | // To meet rust <1.36 borrow checker rules on expr_array.elems |
324 | let v = |
325 | expr_array |
326 | .elems |
327 | .iter() |
328 | .map(|expr| match expr { |
329 | Expr::Lit(lit) => $ty::from_value(&lit.lit), |
330 | _ => Err(Error::custom("Expected array of unsigned integers" ) |
331 | .with_span(expr)), |
332 | }) |
333 | .collect::<Result<Vec<$ty>>>(); |
334 | v |
335 | } |
336 | } |
337 | }; |
338 | } |
339 | |
340 | from_numeric_array!(u8); |
341 | from_numeric_array!(u16); |
342 | from_numeric_array!(u32); |
343 | from_numeric_array!(u64); |
344 | from_numeric_array!(usize); |
345 | |
346 | impl FromMeta for syn::Lit { |
347 | fn from_value(value: &Lit) -> Result<Self> { |
348 | Ok(value.clone()) |
349 | } |
350 | } |
351 | |
352 | macro_rules! from_meta_lit { |
353 | ($impl_ty:path, $lit_variant:path) => { |
354 | impl FromMeta for $impl_ty { |
355 | fn from_value(value: &Lit) -> Result<Self> { |
356 | if let $lit_variant(ref value) = *value { |
357 | Ok(value.clone()) |
358 | } else { |
359 | Err(Error::unexpected_lit_type(value)) |
360 | } |
361 | } |
362 | } |
363 | }; |
364 | } |
365 | |
366 | from_meta_lit!(syn::LitInt, Lit::Int); |
367 | from_meta_lit!(syn::LitFloat, Lit::Float); |
368 | from_meta_lit!(syn::LitStr, Lit::Str); |
369 | from_meta_lit!(syn::LitByte, Lit::Byte); |
370 | from_meta_lit!(syn::LitByteStr, Lit::ByteStr); |
371 | from_meta_lit!(syn::LitChar, Lit::Char); |
372 | from_meta_lit!(syn::LitBool, Lit::Bool); |
373 | from_meta_lit!(proc_macro2::Literal, Lit::Verbatim); |
374 | |
375 | impl FromMeta for syn::Meta { |
376 | fn from_meta(value: &syn::Meta) -> Result<Self> { |
377 | Ok(value.clone()) |
378 | } |
379 | } |
380 | |
381 | impl FromMeta for Vec<syn::WherePredicate> { |
382 | fn from_string(value: &str) -> Result<Self> { |
383 | syn::WhereClause::from_string(&format!("where {}" , value)) |
384 | .map(|c: WhereClause| c.predicates.into_iter().collect()) |
385 | } |
386 | |
387 | fn from_value(value: &Lit) -> Result<Self> { |
388 | if let syn::Lit::Str(s: &LitStr) = value { |
389 | syn::WhereClause::from_value(&syn::Lit::Str(syn::LitStr::new( |
390 | &format!("where {}" , s.value()), |
391 | value.span(), |
392 | ))) |
393 | .map(|c: WhereClause| c.predicates.into_iter().collect()) |
394 | } else { |
395 | Err(Error::unexpected_lit_type(lit:value)) |
396 | } |
397 | } |
398 | } |
399 | |
400 | impl FromMeta for ident_case::RenameRule { |
401 | fn from_string(value: &str) -> Result<Self> { |
402 | value.parse().map_err(|_| Error::unknown_value(value)) |
403 | } |
404 | } |
405 | |
406 | impl<T: FromMeta> FromMeta for Option<T> { |
407 | fn from_none() -> Option<Self> { |
408 | Some(None) |
409 | } |
410 | |
411 | fn from_meta(item: &Meta) -> Result<Self> { |
412 | FromMeta::from_meta(item).map(op:Some) |
413 | } |
414 | } |
415 | |
416 | impl<T: FromMeta> FromMeta for Box<T> { |
417 | fn from_none() -> Option<Self> { |
418 | T::from_none().map(Box::new) |
419 | } |
420 | |
421 | fn from_meta(item: &Meta) -> Result<Self> { |
422 | FromMeta::from_meta(item).map(op:Box::new) |
423 | } |
424 | } |
425 | |
426 | impl<T: FromMeta> FromMeta for Result<T> { |
427 | fn from_none() -> Option<Self> { |
428 | T::from_none().map(Ok) |
429 | } |
430 | |
431 | fn from_meta(item: &Meta) -> Result<Self> { |
432 | Ok(FromMeta::from_meta(item)) |
433 | } |
434 | } |
435 | |
436 | /// Parses the meta-item, and in case of error preserves a copy of the input for |
437 | /// later analysis. |
438 | impl<T: FromMeta> FromMeta for ::std::result::Result<T, Meta> { |
439 | fn from_meta(item: &Meta) -> Result<Self> { |
440 | T::from_meta(item) |
441 | .map(Ok) |
442 | .or_else(|_| Ok(Err(item.clone()))) |
443 | } |
444 | } |
445 | |
446 | impl<T: FromMeta> FromMeta for Rc<T> { |
447 | fn from_none() -> Option<Self> { |
448 | T::from_none().map(Rc::new) |
449 | } |
450 | |
451 | fn from_meta(item: &Meta) -> Result<Self> { |
452 | FromMeta::from_meta(item).map(op:Rc::new) |
453 | } |
454 | } |
455 | |
456 | impl<T: FromMeta> FromMeta for Arc<T> { |
457 | fn from_none() -> Option<Self> { |
458 | T::from_none().map(Arc::new) |
459 | } |
460 | |
461 | fn from_meta(item: &Meta) -> Result<Self> { |
462 | FromMeta::from_meta(item).map(op:Arc::new) |
463 | } |
464 | } |
465 | |
466 | impl<T: FromMeta> FromMeta for RefCell<T> { |
467 | fn from_none() -> Option<Self> { |
468 | T::from_none().map(RefCell::new) |
469 | } |
470 | |
471 | fn from_meta(item: &Meta) -> Result<Self> { |
472 | FromMeta::from_meta(item).map(op:RefCell::new) |
473 | } |
474 | } |
475 | |
476 | /// Trait to convert from a path into an owned key for a map. |
477 | trait KeyFromPath: Sized { |
478 | fn from_path(path: &syn::Path) -> Result<Self>; |
479 | fn to_display(&self) -> Cow<'_, str>; |
480 | } |
481 | |
482 | impl KeyFromPath for String { |
483 | fn from_path(path: &syn::Path) -> Result<Self> { |
484 | Ok(path_to_string(path)) |
485 | } |
486 | |
487 | fn to_display(&self) -> Cow<'_, str> { |
488 | Cow::Borrowed(self) |
489 | } |
490 | } |
491 | |
492 | impl KeyFromPath for syn::Path { |
493 | fn from_path(path: &syn::Path) -> Result<Self> { |
494 | Ok(path.clone()) |
495 | } |
496 | |
497 | fn to_display(&self) -> Cow<'_, str> { |
498 | Cow::Owned(path_to_string(self)) |
499 | } |
500 | } |
501 | |
502 | impl KeyFromPath for syn::Ident { |
503 | fn from_path(path: &syn::Path) -> Result<Self> { |
504 | if path.segments.len() == 1 |
505 | && path.leading_colon.is_none() |
506 | && path.segments[0].arguments.is_empty() |
507 | { |
508 | Ok(path.segments[0].ident.clone()) |
509 | } else { |
510 | Err(Error::custom("Key must be an identifier" ).with_span(node:path)) |
511 | } |
512 | } |
513 | |
514 | fn to_display(&self) -> Cow<'_, str> { |
515 | Cow::Owned(self.to_string()) |
516 | } |
517 | } |
518 | |
519 | macro_rules! hash_map { |
520 | ($key:ty) => { |
521 | impl<V: FromMeta, S: BuildHasher + Default> FromMeta for HashMap<$key, V, S> { |
522 | fn from_list(nested: &[syn::NestedMeta]) -> Result<Self> { |
523 | // Convert the nested meta items into a sequence of (path, value result) result tuples. |
524 | // An outer Err means no (key, value) structured could be found, while an Err in the |
525 | // second position of the tuple means that value was rejected by FromMeta. |
526 | // |
527 | // We defer key conversion into $key so that we don't lose span information in the case |
528 | // of String keys; we'll need it for good duplicate key errors later. |
529 | let pairs = nested |
530 | .iter() |
531 | .map(|item| -> Result<(&syn::Path, Result<V>)> { |
532 | match *item { |
533 | syn::NestedMeta::Meta(ref inner) => { |
534 | let path = inner.path(); |
535 | Ok(( |
536 | path, |
537 | FromMeta::from_meta(inner).map_err(|e| e.at_path(&path)), |
538 | )) |
539 | } |
540 | syn::NestedMeta::Lit(_) => Err(Error::unsupported_format("literal" )), |
541 | } |
542 | }); |
543 | |
544 | let mut errors = Error::accumulator(); |
545 | // We need to track seen keys separately from the final map, since a seen key with an |
546 | // Err value won't go into the final map but should trigger a duplicate field error. |
547 | // |
548 | // This is a set of $key rather than Path to avoid the possibility that a key type |
549 | // parses two paths of different values to the same key value. |
550 | let mut seen_keys = HashSet::with_capacity(nested.len()); |
551 | |
552 | // The map to return in the Ok case. Its size will always be exactly nested.len(), |
553 | // since otherwise ≥1 field had a problem and the entire map is dropped immediately |
554 | // when the function returns `Err`. |
555 | let mut map = HashMap::with_capacity_and_hasher(nested.len(), Default::default()); |
556 | |
557 | for item in pairs { |
558 | if let Some((path, value)) = errors.handle(item) { |
559 | let key: $key = match KeyFromPath::from_path(path) { |
560 | Ok(k) => k, |
561 | Err(e) => { |
562 | errors.push(e); |
563 | |
564 | // Surface value errors even under invalid keys |
565 | errors.handle(value); |
566 | |
567 | continue; |
568 | } |
569 | }; |
570 | |
571 | let already_seen = seen_keys.contains(&key); |
572 | |
573 | if already_seen { |
574 | errors.push(Error::duplicate_field(&key.to_display()).with_span(path)); |
575 | } |
576 | |
577 | match value { |
578 | Ok(_) if already_seen => {} |
579 | Ok(val) => { |
580 | map.insert(key.clone(), val); |
581 | } |
582 | Err(e) => { |
583 | errors.push(e); |
584 | } |
585 | } |
586 | |
587 | seen_keys.insert(key); |
588 | } |
589 | } |
590 | |
591 | errors.finish_with(map) |
592 | } |
593 | } |
594 | }; |
595 | } |
596 | |
597 | // This is done as a macro rather than a blanket impl to avoid breaking backwards compatibility |
598 | // with 0.12.x, while still sharing the same impl. |
599 | hash_map!(String); |
600 | hash_map!(syn::Ident); |
601 | hash_map!(syn::Path); |
602 | |
603 | /// Tests for `FromMeta` implementations. Wherever the word `ignore` appears in test input, |
604 | /// it should not be considered by the parsing. |
605 | #[cfg (test)] |
606 | mod tests { |
607 | use proc_macro2::TokenStream; |
608 | use quote::quote; |
609 | use syn::parse_quote; |
610 | |
611 | use crate::{Error, FromMeta, Result}; |
612 | |
613 | /// parse a string as a syn::Meta instance. |
614 | fn pm(tokens: TokenStream) -> ::std::result::Result<syn::Meta, String> { |
615 | let attribute: syn::Attribute = parse_quote!(#[#tokens]); |
616 | attribute.parse_meta().map_err(|_| "Unable to parse" .into()) |
617 | } |
618 | |
619 | fn fm<T: FromMeta>(tokens: TokenStream) -> T { |
620 | FromMeta::from_meta(&pm(tokens).expect("Tests should pass well-formed input" )) |
621 | .expect("Tests should pass valid input" ) |
622 | } |
623 | |
624 | #[test ] |
625 | fn unit_succeeds() { |
626 | fm::<()>(quote!(ignore)); |
627 | } |
628 | |
629 | #[test ] |
630 | #[allow (clippy::bool_assert_comparison)] |
631 | fn bool_succeeds() { |
632 | // word format |
633 | assert_eq!(fm::<bool>(quote!(ignore)), true); |
634 | |
635 | // bool literal |
636 | assert_eq!(fm::<bool>(quote!(ignore = true)), true); |
637 | assert_eq!(fm::<bool>(quote!(ignore = false)), false); |
638 | |
639 | // string literals |
640 | assert_eq!(fm::<bool>(quote!(ignore = "true" )), true); |
641 | assert_eq!(fm::<bool>(quote!(ignore = "false" )), false); |
642 | } |
643 | |
644 | #[test ] |
645 | fn char_succeeds() { |
646 | // char literal |
647 | assert_eq!(fm::<char>(quote!(ignore = '😬' )), '😬' ); |
648 | |
649 | // string literal |
650 | assert_eq!(fm::<char>(quote!(ignore = "😬" )), '😬' ); |
651 | } |
652 | |
653 | #[test ] |
654 | fn string_succeeds() { |
655 | // cooked form |
656 | assert_eq!(&fm::<String>(quote!(ignore = "world" )), "world" ); |
657 | |
658 | // raw form |
659 | assert_eq!(&fm::<String>(quote!(ignore = r#"world"# )), "world" ); |
660 | } |
661 | |
662 | #[test ] |
663 | #[allow (clippy::float_cmp)] // we want exact equality |
664 | fn number_succeeds() { |
665 | assert_eq!(fm::<u8>(quote!(ignore = "2" )), 2u8); |
666 | assert_eq!(fm::<i16>(quote!(ignore = "-25" )), -25i16); |
667 | assert_eq!(fm::<f64>(quote!(ignore = "1.4e10" )), 1.4e10); |
668 | } |
669 | |
670 | #[test ] |
671 | fn int_without_quotes() { |
672 | assert_eq!(fm::<u8>(quote!(ignore = 2)), 2u8); |
673 | assert_eq!(fm::<u16>(quote!(ignore = 255)), 255u16); |
674 | assert_eq!(fm::<u32>(quote!(ignore = 5000)), 5000u32); |
675 | |
676 | // Check that we aren't tripped up by incorrect suffixes |
677 | assert_eq!(fm::<u32>(quote!(ignore = 5000i32)), 5000u32); |
678 | } |
679 | |
680 | #[test ] |
681 | #[allow (clippy::float_cmp)] // we want exact equality |
682 | fn float_without_quotes() { |
683 | assert_eq!(fm::<f32>(quote!(ignore = 2.)), 2.0f32); |
684 | assert_eq!(fm::<f32>(quote!(ignore = 2.0)), 2.0f32); |
685 | assert_eq!(fm::<f64>(quote!(ignore = 1.4e10)), 1.4e10f64); |
686 | } |
687 | |
688 | #[test ] |
689 | fn meta_succeeds() { |
690 | use syn::Meta; |
691 | |
692 | assert_eq!( |
693 | fm::<Meta>(quote!(hello(world, today))), |
694 | pm(quote!(hello(world, today))).unwrap() |
695 | ); |
696 | } |
697 | |
698 | #[test ] |
699 | fn hash_map_succeeds() { |
700 | use std::collections::HashMap; |
701 | |
702 | let comparison = { |
703 | let mut c = HashMap::new(); |
704 | c.insert("hello" .to_string(), true); |
705 | c.insert("world" .to_string(), false); |
706 | c.insert("there" .to_string(), true); |
707 | c |
708 | }; |
709 | |
710 | assert_eq!( |
711 | fm::<HashMap<String, bool>>(quote!(ignore(hello, world = false, there = "true" ))), |
712 | comparison |
713 | ); |
714 | } |
715 | |
716 | /// Check that a `HashMap` cannot have duplicate keys, and that the generated error |
717 | /// is assigned a span to correctly target the diagnostic message. |
718 | #[test ] |
719 | fn hash_map_duplicate() { |
720 | use std::collections::HashMap; |
721 | |
722 | let err: Result<HashMap<String, bool>> = |
723 | FromMeta::from_meta(&pm(quote!(ignore(hello, hello = false))).unwrap()); |
724 | |
725 | let err = err.expect_err("Duplicate keys in HashMap should error" ); |
726 | |
727 | assert!(err.has_span()); |
728 | assert_eq!(err.to_string(), Error::duplicate_field("hello" ).to_string()); |
729 | } |
730 | |
731 | #[test ] |
732 | fn hash_map_multiple_errors() { |
733 | use std::collections::HashMap; |
734 | |
735 | let err = HashMap::<String, bool>::from_meta( |
736 | &pm(quote!(ignore(hello, hello = 3, hello = false))).unwrap(), |
737 | ) |
738 | .expect_err("Duplicates and bad values should error" ); |
739 | |
740 | assert_eq!(err.len(), 3); |
741 | let errors = err.into_iter().collect::<Vec<_>>(); |
742 | assert!(errors[0].has_span()); |
743 | assert!(errors[1].has_span()); |
744 | assert!(errors[2].has_span()); |
745 | } |
746 | |
747 | #[test ] |
748 | fn hash_map_ident_succeeds() { |
749 | use std::collections::HashMap; |
750 | use syn::parse_quote; |
751 | |
752 | let comparison = { |
753 | let mut c = HashMap::<syn::Ident, bool>::new(); |
754 | c.insert(parse_quote!(first), true); |
755 | c.insert(parse_quote!(second), false); |
756 | c |
757 | }; |
758 | |
759 | assert_eq!( |
760 | fm::<HashMap<syn::Ident, bool>>(quote!(ignore(first, second = false))), |
761 | comparison |
762 | ); |
763 | } |
764 | |
765 | #[test ] |
766 | fn hash_map_ident_rejects_non_idents() { |
767 | use std::collections::HashMap; |
768 | |
769 | let err: Result<HashMap<syn::Ident, bool>> = |
770 | FromMeta::from_meta(&pm(quote!(ignore(first, the::second))).unwrap()); |
771 | |
772 | err.unwrap_err(); |
773 | } |
774 | |
775 | #[test ] |
776 | fn hash_map_path_succeeds() { |
777 | use std::collections::HashMap; |
778 | use syn::parse_quote; |
779 | |
780 | let comparison = { |
781 | let mut c = HashMap::<syn::Path, bool>::new(); |
782 | c.insert(parse_quote!(first), true); |
783 | c.insert(parse_quote!(the::second), false); |
784 | c |
785 | }; |
786 | |
787 | assert_eq!( |
788 | fm::<HashMap<syn::Path, bool>>(quote!(ignore(first, the::second = false))), |
789 | comparison |
790 | ); |
791 | } |
792 | |
793 | /// Tests that fallible parsing will always produce an outer `Ok` (from `fm`), |
794 | /// and will accurately preserve the inner contents. |
795 | #[test ] |
796 | fn darling_result_succeeds() { |
797 | fm::<Result<()>>(quote!(ignore)).unwrap(); |
798 | fm::<Result<()>>(quote!(ignore(world))).unwrap_err(); |
799 | } |
800 | |
801 | /// Test punctuated |
802 | #[test ] |
803 | fn test_punctuated() { |
804 | fm::<syn::punctuated::Punctuated<syn::FnArg, syn::token::Comma>>(quote!( |
805 | ignore = "a: u8, b: Type" |
806 | )); |
807 | fm::<syn::punctuated::Punctuated<syn::Expr, syn::token::Comma>>(quote!(ignore = "a, b, c" )); |
808 | } |
809 | |
810 | #[test ] |
811 | fn test_expr_array() { |
812 | fm::<syn::ExprArray>(quote!(ignore = "[0x1, 0x2]" )); |
813 | fm::<syn::ExprArray>(quote!(ignore = "[ \"Hello World \", \"Test Array \"]" )); |
814 | } |
815 | |
816 | #[test ] |
817 | fn test_expr() { |
818 | fm::<syn::Expr>(quote!(ignore = "x + y" )); |
819 | fm::<syn::Expr>(quote!(ignore = "an_object.method_call()" )); |
820 | fm::<syn::Expr>(quote!(ignore = "{ a_statement(); in_a_block }" )); |
821 | } |
822 | |
823 | #[test ] |
824 | fn test_expr_path() { |
825 | fm::<syn::ExprPath>(quote!(ignore = "std::mem::replace" )); |
826 | fm::<syn::ExprPath>(quote!(ignore = "x" )); |
827 | fm::<syn::ExprPath>(quote!(ignore = "example::<Test>" )); |
828 | } |
829 | |
830 | #[test ] |
831 | fn test_number_array() { |
832 | assert_eq!( |
833 | fm::<Vec<u8>>(quote!(ignore = "[16, 0xff]" )), |
834 | vec![0x10, 0xff] |
835 | ); |
836 | assert_eq!( |
837 | fm::<Vec<u16>>(quote!(ignore = "[32, 0xffff]" )), |
838 | vec![0x20, 0xffff] |
839 | ); |
840 | assert_eq!( |
841 | fm::<Vec<u32>>(quote!(ignore = "[48, 0xffffffff]" )), |
842 | vec![0x30, 0xffffffff] |
843 | ); |
844 | assert_eq!( |
845 | fm::<Vec<u64>>(quote!(ignore = "[64, 0xffffffffffffffff]" )), |
846 | vec![0x40, 0xffffffffffffffff] |
847 | ); |
848 | assert_eq!( |
849 | fm::<Vec<usize>>(quote!(ignore = "[80, 0xffffffff]" )), |
850 | vec![0x50, 0xffffffff] |
851 | ); |
852 | } |
853 | } |
854 | |