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