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