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