1 use std::borrow::Cow;
2 use std::cell::RefCell;
3 use std::collections::hash_map::HashMap;
4 use std::collections::HashSet;
5 use std::hash::BuildHasher;
6 use std::rc::Rc;
7 use std::sync::atomic::AtomicBool;
8 use std::sync::Arc;
9
10 use ident_case;
11 use syn::{self, Expr, Lit, Meta, NestedMeta};
12
13 use {Error, Result};
14
15 /// Create an instance from an item in an attribute declaration.
16 ///
17 /// # Implementing `FromMeta`
18 /// * 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.
19 /// * Implement only the `from_*` methods that you intend to support. The default implementations will return useful errors.
20 ///
21 /// # Provided Implementations
22 /// ## bool
23 ///
24 /// * Word with no value specified - becomes `true`.
25 /// * As a boolean literal, e.g. `foo = true`.
26 /// * As a string literal, e.g. `foo = "true"`.
27 ///
28 /// ## char
29 /// * As a char literal, e.g. `foo = '#'`.
30 /// * As a string literal consisting of a single character, e.g. `foo = "#"`.
31 ///
32 /// ## String
33 /// * As a string literal, e.g. `foo = "hello"`.
34 /// * As a raw string literal, e.g. `foo = r#"hello "world""#`.
35 ///
36 /// ## Number
37 /// * As a string literal, e.g. `foo = "-25"`.
38 /// * As an unquoted positive value, e.g. `foo = 404`. Negative numbers must be in quotation marks.
39 ///
40 /// ## ()
41 /// * Word with no value specified, e.g. `foo`. This is best used with `Option`.
42 /// See `darling::util::Flag` for a more strongly-typed alternative.
43 ///
44 /// ## Option
45 /// * Any format produces `Some`.
46 ///
47 /// ## `Result<T, darling::Error>`
48 /// * Allows for fallible parsing; will populate the target field with the result of the
49 /// parse attempt.
50 pub trait FromMeta: Sized {
51 fn from_nested_meta(item: &NestedMeta) -> Result<Self> {
52 (match *item {
53 NestedMeta::Lit(ref lit) => Self::from_value(lit),
54 NestedMeta::Meta(ref mi) => Self::from_meta(mi),
55 })
56 .map_err(|e| e.with_span(item))
57 }
58
59 /// Create an instance from a `syn::Meta` by dispatching to the format-appropriate
60 /// trait function. This generally should not be overridden by implementers.
61 ///
62 /// # Error Spans
63 /// If this method is overridden and can introduce errors that weren't passed up from
64 /// other `from_meta` calls, the override must call `with_span` on the error using the
65 /// `item` to make sure that the emitted diagnostic points to the correct location in
66 /// source code.
67 fn from_meta(item: &Meta) -> Result<Self> {
68 (match *item {
69 Meta::Path(_) => Self::from_word(),
70 Meta::List(ref value) => Self::from_list(
71 &value
72 .nested
73 .iter()
74 .cloned()
75 .collect::<Vec<syn::NestedMeta>>()[..],
76 ),
77 Meta::NameValue(ref value) => Self::from_value(&value.lit),
78 })
79 .map_err(|e| e.with_span(item))
80 }
G_DEFINE_TYPE_WITH_CODE(GladePlaceholder,glade_placeholder,GTK_TYPE_WIDGET,G_ADD_PRIVATE (GladePlaceholder)G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE,NULL)G_IMPLEMENT_INTERFACE (GLADE_TYPE_DRAG,glade_placeholder_drag_init))81
82 /// Create an instance from the presence of the word in the attribute with no
83 /// additional options specified.
84 fn from_word() -> Result<Self> {
85 Err(Error::unsupported_format("word"))
86 }
87
88 /// Create an instance from a list of nested meta items.
89 #[allow(unused_variables)]
90 fn from_list(items: &[NestedMeta]) -> Result<Self> {
91 Err(Error::unsupported_format("list"))
92 }
93
94 /// Create an instance from a literal value of either `foo = "bar"` or `foo("bar")`.
95 /// This dispatches to the appropriate method based on the type of literal encountered,
96 /// and generally should not be overridden by implementers.
97 ///
98 /// # Error Spans
99 /// If this method is overridden, the override must make sure to add `value`'s span
100 /// information to the returned error by calling `with_span(value)` on the `Error` instance.
101 fn from_value(value: &Lit) -> Result<Self> {
102 (match *value {
103 Lit::Bool(ref b) => Self::from_bool(b.value),
104 Lit::Str(ref s) => Self::from_string(&s.value()),
105 Lit::Char(ref ch) => Self::from_char(ch.value()),
106 _ => Err(Error::unexpected_lit_type(value)),
107 })
108 .map_err(|e| e.with_span(value))
109 }
110
111 /// Create an instance from a char literal in a value position.
112 #[allow(unused_variables)]
glade_placeholder_init(GladePlaceholder * placeholder)113 fn from_char(value: char) -> Result<Self> {
114 Err(Error::unexpected_type("char"))
115 }
116
117 /// Create an instance from a string literal in a value position.
118 #[allow(unused_variables)]
119 fn from_string(value: &str) -> Result<Self> {
120 Err(Error::unexpected_type("string"))
121 }
122
123 /// Create an instance from a bool literal in a value position.
124 #[allow(unused_variables)]
125 fn from_bool(value: bool) -> Result<Self> {
126 Err(Error::unexpected_type("bool"))
127 }
128 }
129
130 // FromMeta impls for std and syn types.
131
132 impl FromMeta for () {
133 fn from_word() -> Result<Self> {
134 Ok(())
135 }
glade_placeholder_finalize(GObject * object)136 }
137
138 impl FromMeta for bool {
139 fn from_word() -> Result<Self> {
140 Ok(true)
141 }
142
143 fn from_bool(value: bool) -> Result<Self> {
144 Ok(value)
145 }
146
147 fn from_string(value: &str) -> Result<Self> {
148 value.parse().map_err(|_| Error::unknown_value(value))
149 }
150 }
151
152 impl FromMeta for AtomicBool {
glade_placeholder_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)153 fn from_meta(mi: &Meta) -> Result<Self> {
154 FromMeta::from_meta(mi)
155 .map(AtomicBool::new)
156 .map_err(|e| e.with_span(mi))
157 }
158 }
159
160 impl FromMeta for char {
161 fn from_char(value: char) -> Result<Self> {
162 Ok(value)
163 }
164
165 fn from_string(s: &str) -> Result<Self> {
166 let mut chars = s.chars();
167 let char1 = chars.next();
168 let char2 = chars.next();
169
170 if let (Some(char), None) = (char1, char2) {
171 Ok(char)
172 } else {
glade_placeholder_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)173 Err(Error::unexpected_type("string"))
174 }
175 }
176 }
177
178 impl FromMeta for String {
179 fn from_string(s: &str) -> Result<Self> {
180 Ok(s.to_string())
181 }
182 }
183
184 /// Generate an impl of `FromMeta` that will accept strings which parse to numbers or
185 /// integer literals.
186 macro_rules! from_meta_num {
187 ($ty:ident) => {
188 impl FromMeta for $ty {
189 fn from_string(s: &str) -> Result<Self> {
190 s.parse().map_err(|_| Error::unknown_value(s))
191 }
192
193 fn from_value(value: &Lit) -> Result<Self> {
194 (match *value {
glade_placeholder_realize(GtkWidget * widget)195 Lit::Str(ref s) => Self::from_string(&s.value()),
196 Lit::Int(ref s) => Ok(s.base10_parse::<$ty>().unwrap()),
197 _ => Err(Error::unexpected_lit_type(value)),
198 })
199 .map_err(|e| e.with_span(value))
200 }
201 }
202 };
203 }
204
205 from_meta_num!(u8);
206 from_meta_num!(u16);
207 from_meta_num!(u32);
208 from_meta_num!(u64);
209 from_meta_num!(usize);
210 from_meta_num!(i8);
211 from_meta_num!(i16);
212 from_meta_num!(i32);
213 from_meta_num!(i64);
214 from_meta_num!(isize);
215
216 /// Generate an impl of `FromMeta` that will accept strings which parse to floats or
217 /// float literals.
218 macro_rules! from_meta_float {
219 ($ty:ident) => {
220 impl FromMeta for $ty {
221 fn from_string(s: &str) -> Result<Self> {
222 s.parse().map_err(|_| Error::unknown_value(s))
223 }
224
225 fn from_value(value: &Lit) -> Result<Self> {
226 (match *value {
227 Lit::Str(ref s) => Self::from_string(&s.value()),
228 Lit::Float(ref s) => Ok(s.base10_parse::<$ty>().unwrap()),
229 _ => Err(Error::unexpected_lit_type(value)),
230 })
231 .map_err(|e| e.with_span(value))
232 }
glade_placeholder_unrealize(GtkWidget * widget)233 }
234 };
235 }
236
237 from_meta_float!(f32);
238 from_meta_float!(f64);
239
240 /// Parsing support for identifiers. This attempts to preserve span information
241 /// when available, but also supports parsing strings with the call site as the
242 /// emitted span.
243 impl FromMeta for syn::Ident {
244 fn from_string(value: &str) -> Result<Self> {
245 Ok(syn::Ident::new(value, ::proc_macro2::Span::call_site()))
246 }
247
248 fn from_value(value: &Lit) -> Result<Self> {
249 if let Lit::Str(ref ident) = *value {
glade_placeholder_map(GtkWidget * widget)250 ident
251 .parse()
252 .map_err(|_| Error::unknown_lit_str_value(ident))
253 } else {
254 Err(Error::unexpected_lit_type(value))
255 }
256 }
257 }
258
259 /// Parsing support for punctuated. This attempts to preserve span information
260 /// when available, but also supports parsing strings with the call site as the
261 /// emitted span.
262 impl<T: syn::parse::Parse, P: syn::parse::Parse> FromMeta for syn::punctuated::Punctuated<T, P> {
263 fn from_value(value: &Lit) -> Result<Self> {
264 if let Lit::Str(ref ident) = *value {
glade_placeholder_unmap(GtkWidget * widget)265 ident
266 .parse_with(syn::punctuated::Punctuated::parse_terminated)
267 .map_err(|_| Error::unknown_lit_str_value(ident))
268 } else {
269 Err(Error::unexpected_lit_type(value))
270 }
271 }
272 }
273
274 /// Parsing support for an array, i.e. `example = "[1 + 2, 2 - 2, 3 * 4]"`.
275 impl FromMeta for syn::ExprArray {
276 fn from_value(value: &Lit) -> Result<Self> {
277 if let Lit::Str(ref ident) = *value {
278 ident
279 .parse::<syn::ExprArray>()
glade_placeholder_size_allocate(GtkWidget * widget,GtkAllocation * allocation)280 .map_err(|_| Error::unknown_lit_str_value(ident))
281 } else {
282 Err(Error::unexpected_lit_type(value))
283 }
284 }
285 }
286
287 macro_rules! from_numeric_array {
288 ($ty:ident) => {
289 /// Parsing an unsigned integer array, i.e. `example = "[1, 2, 3, 4]"`.
290 impl FromMeta for Vec<$ty> {
291 fn from_value(value: &Lit) -> Result<Self> {
292 let expr_array = syn::ExprArray::from_value(value)?;
293 // To meet rust <1.36 borrow checker rules on expr_array.elems
294 let v =
295 expr_array
296 .elems
glade_placeholder_draw(GtkWidget * widget,cairo_t * cr)297 .iter()
298 .map(|expr| match expr {
299 Expr::Lit(lit) => $ty::from_value(&lit.lit),
300 _ => Err(Error::custom("Expected array of unsigned integers")
301 .with_span(expr)),
302 })
303 .collect::<Result<Vec<$ty>>>();
304 v
305 }
306 }
307 };
308 }
309
310 from_numeric_array!(u8);
311 from_numeric_array!(u16);
312 from_numeric_array!(u32);
313 from_numeric_array!(u64);
314 from_numeric_array!(usize);
315
316 /// Parsing support for paths. This attempts to preserve span information when available,
317 /// but also supports parsing strings with the call site as the emitted span.
318 impl FromMeta for syn::Path {
319 fn from_string(value: &str) -> Result<Self> {
320 syn::parse_str(value).map_err(|_| Error::unknown_value(value))
321 }
322
323 fn from_value(value: &Lit) -> Result<Self> {
324 if let Lit::Str(ref path_str) = *value {
325 path_str
326 .parse()
327 .map_err(|_| Error::unknown_lit_str_value(path_str))
328 } else {
329 Err(Error::unexpected_lit_type(value))
330 }
331 }
332 }
333
334 impl FromMeta for syn::Lit {
335 fn from_value(value: &Lit) -> Result<Self> {
336 Ok(value.clone())
337 }
338 }
339
340 macro_rules! from_meta_lit {
341 ($impl_ty:path, $lit_variant:path) => {
342 impl FromMeta for $impl_ty {
343 fn from_value(value: &Lit) -> Result<Self> {
344 if let $lit_variant(ref value) = *value {
345 Ok(value.clone())
346 } else {
347 Err(Error::unexpected_lit_type(value))
348 }
349 }
350 }
351 };
352 }
353
354 from_meta_lit!(syn::LitInt, Lit::Int);
355 from_meta_lit!(syn::LitFloat, Lit::Float);
356 from_meta_lit!(syn::LitStr, Lit::Str);
357 from_meta_lit!(syn::LitByte, Lit::Byte);
358 from_meta_lit!(syn::LitByteStr, Lit::ByteStr);
359 from_meta_lit!(syn::LitChar, Lit::Char);
360 from_meta_lit!(syn::LitBool, Lit::Bool);
361 from_meta_lit!(proc_macro2::Literal, Lit::Verbatim);
glade_placeholder_update_cursor(GladePlaceholder * placeholder,GdkWindow * win)362
363 impl FromMeta for syn::Meta {
364 fn from_meta(value: &syn::Meta) -> Result<Self> {
365 Ok(value.clone())
366 }
367 }
368
369 impl FromMeta for syn::WhereClause {
370 fn from_string(value: &str) -> Result<Self> {
371 syn::parse_str(value).map_err(|_| Error::unknown_value(value))
372 }
373 }
glade_placeholder_enter_notify_event(GtkWidget * widget,GdkEventCrossing * event)374
375 impl FromMeta for Vec<syn::WherePredicate> {
376 fn from_string(value: &str) -> Result<Self> {
377 syn::WhereClause::from_string(&format!("where {}", value))
378 .map(|c| c.predicates.into_iter().collect())
379 }
380 }
381
glade_placeholder_motion_notify_event(GtkWidget * widget,GdkEventMotion * event)382 impl FromMeta for ident_case::RenameRule {
383 fn from_string(value: &str) -> Result<Self> {
384 value.parse().map_err(|_| Error::unknown_value(value))
385 }
386 }
387
388 impl<T: FromMeta> FromMeta for Option<T> {
on_chooser_adaptor_widget_selected(_GladeAdaptorChooserWidget * chooser,GladeWidgetAdaptor * adaptor,GladePlaceholder * placeholder)389 fn from_meta(item: &Meta) -> Result<Self> {
390 FromMeta::from_meta(item).map(Some)
391 }
392 }
393
394 impl<T: FromMeta> FromMeta for Box<T> {
395 fn from_meta(item: &Meta) -> Result<Self> {
396 FromMeta::from_meta(item).map(Box::new)
397 }
398 }
399
glade_placeholder_popover_new(GladePlaceholder * placeholder,GtkWidget * relative_to)400 impl<T: FromMeta> FromMeta for Result<T> {
401 fn from_meta(item: &Meta) -> Result<Self> {
402 Ok(FromMeta::from_meta(item))
403 }
404 }
405
406 /// Parses the meta-item, and in case of error preserves a copy of the input for
407 /// later analysis.
408 impl<T: FromMeta> FromMeta for ::std::result::Result<T, Meta> {
409 fn from_meta(item: &Meta) -> Result<Self> {
410 T::from_meta(item)
411 .map(Ok)
412 .or_else(|_| Ok(Err(item.clone())))
413 }
414 }
415
416 impl<T: FromMeta> FromMeta for Rc<T> {
417 fn from_meta(item: &Meta) -> Result<Self> {
418 FromMeta::from_meta(item).map(Rc::new)
419 }
420 }
glade_placeholder_button_press(GtkWidget * widget,GdkEventButton * event)421
422 impl<T: FromMeta> FromMeta for Arc<T> {
423 fn from_meta(item: &Meta) -> Result<Self> {
424 FromMeta::from_meta(item).map(Arc::new)
425 }
426 }
427
428 impl<T: FromMeta> FromMeta for RefCell<T> {
429 fn from_meta(item: &Meta) -> Result<Self> {
430 FromMeta::from_meta(item).map(RefCell::new)
431 }
432 }
433
434 fn path_to_string(path: &syn::Path) -> String {
435 path.segments
436 .iter()
437 .map(|s| s.ident.to_string())
438 .collect::<Vec<String>>()
439 .join("::")
440 }
441
442 /// Trait to convert from a path into an owned key for a map.
443 trait KeyFromPath: Sized {
444 fn from_path(path: &syn::Path) -> Result<Self>;
445 fn to_display<'a>(&'a self) -> Cow<'a, str>;
446 }
447
448 impl KeyFromPath for String {
449 fn from_path(path: &syn::Path) -> Result<Self> {
450 Ok(path_to_string(path))
451 }
452
453 fn to_display<'a>(&'a self) -> Cow<'a, str> {
454 Cow::Borrowed(&self)
455 }
456 }
457
458 impl KeyFromPath for syn::Path {
459 fn from_path(path: &syn::Path) -> Result<Self> {
460 Ok(path.clone())
461 }
462
463 fn to_display<'a>(&'a self) -> Cow<'a, str> {
464 Cow::Owned(path_to_string(self))
465 }
466 }
467
468 impl KeyFromPath for syn::Ident {
469 fn from_path(path: &syn::Path) -> Result<Self> {
470 if path.segments.len() == 1
471 && path.leading_colon.is_none()
472 && path.segments[0].arguments.is_empty()
473 {
474 Ok(path.segments[0].ident.clone())
475 } else {
476 Err(Error::custom("Key must be an identifier").with_span(path))
477 }
478 }
479
480 fn to_display<'a>(&'a self) -> Cow<'a, str> {
481 Cow::Owned(self.to_string())
482 }
483 }
484
glade_placeholder_popup_menu(GtkWidget * widget)485 macro_rules! hash_map {
486 ($key:ty) => {
487 impl<V: FromMeta, S: BuildHasher + Default> FromMeta for HashMap<$key, V, S> {
488 fn from_list(nested: &[syn::NestedMeta]) -> Result<Self> {
489 // Convert the nested meta items into a sequence of (path, value result) result tuples.
490 // An outer Err means no (key, value) structured could be found, while an Err in the
491 // second position of the tuple means that value was rejected by FromMeta.
492 //
493 // We defer key conversion into $key so that we don't lose span information in the case
494 // of String keys; we'll need it for good duplicate key errors later.
495 let pairs = nested
496 .iter()
497 .map(|item| -> Result<(&syn::Path, Result<V>)> {
498 match *item {
499 syn::NestedMeta::Meta(ref inner) => {
500 let path = inner.path();
501 Ok((
502 path,
503 FromMeta::from_meta(inner).map_err(|e| e.at_path(&path)),
504 ))
505 }
506 syn::NestedMeta::Lit(_) => Err(Error::unsupported_format("literal")),
507 }
508 });
509
510 let mut errors = vec![];
511 // We need to track seen keys separately from the final map, since a seen key with an
512 // Err value won't go into the final map but should trigger a duplicate field error.
513 //
514 // This is a set of $key rather than Path to avoid the possibility that a key type
515 // parses two paths of different values to the same key value.
516 let mut seen_keys = HashSet::with_capacity(nested.len());
517
518 // The map to return in the Ok case. Its size will always be exactly nested.len(),
519 // since otherwise ≥1 field had a problem and the entire map is dropped immediately
520 // when the function returns `Err`.
521 let mut map = HashMap::with_capacity_and_hasher(nested.len(), Default::default());
522
523 for item in pairs {
524 match item {
525 Ok((path, value)) => {
526 let key: $key = match KeyFromPath::from_path(path) {
527 Ok(k) => k,
528 Err(e) => {
529 errors.push(e);
530
531 // Surface value errors even under invalid keys
532 if let Err(val_err) = value {
533 errors.push(val_err);
534 }
535
536 continue;
537 }
538 };
539
540 let already_seen = seen_keys.contains(&key);
541
542 if already_seen {
543 errors.push(
544 Error::duplicate_field(&key.to_display()).with_span(path),
545 );
546 }
547
548 match value {
549 Ok(_) if already_seen => {}
550 Ok(val) => {
551 map.insert(key.clone(), val);
552 }
553 Err(e) => {
554 errors.push(e);
555 }
556 }
557
558 seen_keys.insert(key);
559 }
560 Err(e) => {
561 errors.push(e);
562 }
563 }
564 }
565
566 if !errors.is_empty() {
567 return Err(Error::multiple(errors));
568 }
569
570 Ok(map)
571 }
572 }
573 };
574 }
575
576 // This is done as a macro rather than a blanket impl to avoid breaking backwards compatibility
577 // with 0.12.x, while still sharing the same impl.
glade_placeholder_drag_init(_GladeDragInterface * iface)578 hash_map!(String);
579 hash_map!(syn::Ident);
580 hash_map!(syn::Path);
581
582 /// Tests for `FromMeta` implementations. Wherever the word `ignore` appears in test input,
583 /// it should not be considered by the parsing.
584 #[cfg(test)]
585 mod tests {
586 use proc_macro2::TokenStream;
glade_placeholder_class_init(GladePlaceholderClass * klass)587 use syn;
588
589 use {Error, FromMeta, Result};
590
591 /// parse a string as a syn::Meta instance.
592 fn pm(tokens: TokenStream) -> ::std::result::Result<syn::Meta, String> {
593 let attribute: syn::Attribute = parse_quote!(#[#tokens]);
594 attribute.parse_meta().map_err(|_| "Unable to parse".into())
595 }
596
597 fn fm<T: FromMeta>(tokens: TokenStream) -> T {
598 FromMeta::from_meta(&pm(tokens).expect("Tests should pass well-formed input"))
599 .expect("Tests should pass valid input")
600 }
601
602 #[test]
603 fn unit_succeeds() {
604 assert_eq!(fm::<()>(quote!(ignore)), ());
605 }
606
607 #[test]
608 fn bool_succeeds() {
609 // word format
610 assert_eq!(fm::<bool>(quote!(ignore)), true);
611
612 // bool literal
613 assert_eq!(fm::<bool>(quote!(ignore = true)), true);
614 assert_eq!(fm::<bool>(quote!(ignore = false)), false);
615
616 // string literals
617 assert_eq!(fm::<bool>(quote!(ignore = "true")), true);
618 assert_eq!(fm::<bool>(quote!(ignore = "false")), false);
619 }
620
621 #[test]
622 fn char_succeeds() {
623 // char literal
624 assert_eq!(fm::<char>(quote!(ignore = '')), '');
625
626 // string literal
627 assert_eq!(fm::<char>(quote!(ignore = "")), '');
628 }
629
630 #[test]
631 fn string_succeeds() {
632 // cooked form
633 assert_eq!(&fm::<String>(quote!(ignore = "world")), "world");
634
635 // raw form
636 assert_eq!(&fm::<String>(quote!(ignore = r#"world"#)), "world");
637 }
638
glade_placeholder_new(void)639 #[test]
640 fn number_succeeds() {
641 assert_eq!(fm::<u8>(quote!(ignore = "2")), 2u8);
642 assert_eq!(fm::<i16>(quote!(ignore = "-25")), -25i16);
643 assert_eq!(fm::<f64>(quote!(ignore = "1.4e10")), 1.4e10);
644 }
glade_placeholder_get_project(GladePlaceholder * placeholder)645
646 #[test]
647 fn int_without_quotes() {
648 assert_eq!(fm::<u8>(quote!(ignore = 2)), 2u8);
649 assert_eq!(fm::<u16>(quote!(ignore = 255)), 255u16);
650 assert_eq!(fm::<u32>(quote!(ignore = 5000)), 5000u32);
651
652 // Check that we aren't tripped up by incorrect suffixes
653 assert_eq!(fm::<u32>(quote!(ignore = 5000i32)), 5000u32);
654 }
655
656 #[test]
657 fn float_without_quotes() {
658 assert_eq!(fm::<f32>(quote!(ignore = 2.)), 2.0f32);
659 assert_eq!(fm::<f32>(quote!(ignore = 2.0)), 2.0f32);
660 assert_eq!(fm::<f64>(quote!(ignore = 1.4e10)), 1.4e10f64);
661 }
662
663 #[test]
664 fn meta_succeeds() {
665 use syn::Meta;
666
667 assert_eq!(
668 fm::<Meta>(quote!(hello(world, today))),
669 pm(quote!(hello(world, today))).unwrap()
glade_placeholder_packing_actions(GladePlaceholder * placeholder)670 );
671 }
672
673 #[test]
674 fn hash_map_succeeds() {
675 use std::collections::HashMap;
676
677 let comparison = {
678 let mut c = HashMap::new();
679 c.insert("hello".to_string(), true);
680 c.insert("world".to_string(), false);
681 c.insert("there".to_string(), true);
682 c
683 };
684
685 assert_eq!(
686 fm::<HashMap<String, bool>>(quote!(ignore(hello, world = false, there = "true"))),
687 comparison
688 );
689 }
690
691 /// Check that a `HashMap` cannot have duplicate keys, and that the generated error
692 /// is assigned a span to correctly target the diagnostic message.
693 #[test]
694 fn hash_map_duplicate() {
695 use std::collections::HashMap;
696
697 let err: Result<HashMap<String, bool>> =
698 FromMeta::from_meta(&pm(quote!(ignore(hello, hello = false))).unwrap());
699
700 let err = err.expect_err("Duplicate keys in HashMap should error");
701
702 assert!(err.has_span());
703 assert_eq!(err.to_string(), Error::duplicate_field("hello").to_string());
704 }
705
706 #[test]
707 fn hash_map_multiple_errors() {
708 use std::collections::HashMap;
709
710 let err = HashMap::<String, bool>::from_meta(
711 &pm(quote!(ignore(hello, hello = 3, hello = false))).unwrap(),
712 )
713 .expect_err("Duplicates and bad values should error");
714
715 assert_eq!(err.len(), 3);
716 let errors = err.into_iter().collect::<Vec<_>>();
717 assert!(errors[0].has_span());
718 assert!(errors[1].has_span());
719 assert!(errors[2].has_span());
720 }
721
722 #[test]
723 fn hash_map_ident_succeeds() {
724 use std::collections::HashMap;
725 use syn::parse_quote;
726
727 let comparison = {
728 let mut c = HashMap::<syn::Ident, bool>::new();
729 c.insert(parse_quote!(first), true);
730 c.insert(parse_quote!(second), false);
731 c
732 };
733
734 assert_eq!(
735 fm::<HashMap<syn::Ident, bool>>(quote!(ignore(first, second = false))),
736 comparison
737 );
738 }
739
740 #[test]
741 fn hash_map_ident_rejects_non_idents() {
742 use std::collections::HashMap;
743
744 let err: Result<HashMap<syn::Ident, bool>> =
745 FromMeta::from_meta(&pm(quote!(ignore(first, the::second))).unwrap());
746
747 err.unwrap_err();
748 }
749
750 #[test]
751 fn hash_map_path_succeeds() {
752 use std::collections::HashMap;
753 use syn::parse_quote;
754
755 let comparison = {
756 let mut c = HashMap::<syn::Path, bool>::new();
757 c.insert(parse_quote!(first), true);
758 c.insert(parse_quote!(the::second), false);
759 c
760 };
761
762 assert_eq!(
763 fm::<HashMap<syn::Path, bool>>(quote!(ignore(first, the::second = false))),
764 comparison
765 );
766 }
767
768 /// Tests that fallible parsing will always produce an outer `Ok` (from `fm`),
769 /// and will accurately preserve the inner contents.
770 #[test]
771 fn darling_result_succeeds() {
772 fm::<Result<()>>(quote!(ignore)).unwrap();
773 fm::<Result<()>>(quote!(ignore(world))).unwrap_err();
774 }
775
776 /// Test punctuated
777 #[test]
778 fn test_punctuated() {
779 fm::<syn::punctuated::Punctuated<syn::FnArg, syn::token::Comma>>(quote!(
780 ignore = "a: u8, b: Type"
781 ));
782 fm::<syn::punctuated::Punctuated<syn::Expr, syn::token::Comma>>(quote!(ignore = "a, b, c"));
783 }
784
785 #[test]
786 fn test_expr_array() {
787 fm::<syn::ExprArray>(quote!(ignore = "[0x1, 0x2]"));
788 fm::<syn::ExprArray>(quote!(ignore = "[\"Hello World\", \"Test Array\"]"));
789 }
790
791 #[test]
792 fn test_number_array() {
793 assert_eq!(
794 fm::<Vec<u8>>(quote!(ignore = "[16, 0xff]")),
795 vec![0x10, 0xff]
796 );
797 assert_eq!(
798 fm::<Vec<u16>>(quote!(ignore = "[32, 0xffff]")),
799 vec![0x20, 0xffff]
800 );
801 assert_eq!(
802 fm::<Vec<u32>>(quote!(ignore = "[48, 0xffffffff]")),
803 vec![0x30, 0xffffffff]
804 );
805 assert_eq!(
806 fm::<Vec<u64>>(quote!(ignore = "[64, 0xffffffffffffffff]")),
807 vec![0x40, 0xffffffffffffffff]
808 );
809 assert_eq!(
810 fm::<Vec<usize>>(quote!(ignore = "[80, 0xffffffff]")),
811 vec![0x50, 0xffffffff]
812 );
813 }
814 }
815