| 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 = chars.next(); | 
|---|
| 198 | let char2: Option = chars.next(); | 
|---|
| 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 |  | 
|---|