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