Revision control
Copy as Markdown
Other Tools
use darling::{util::Flag, FromDeriveInput, FromMeta};
use proc_macro2::Ident;
use syn::parse_quote;
#[derive(FromMeta)]
struct Vis {
public: Flag,
private: Flag,
}
#[derive(FromDeriveInput)]
#[darling(attributes(sample))]
struct Example {
ident: Ident,
label: String,
#[darling(flatten)]
visibility: Vis,
}
#[test]
fn happy_path() {
let di = Example::from_derive_input(&parse_quote! {
#[sample(label = "Hello", public)]
struct Demo {}
});
let parsed = di.unwrap();
assert_eq!(parsed.ident, "Demo");
assert_eq!(&parsed.label, "Hello");
assert!(parsed.visibility.public.is_present());
assert!(!parsed.visibility.private.is_present());
}
#[test]
fn unknown_field_errors() {
let errors = Example::from_derive_input(&parse_quote! {
#[sample(label = "Hello", republic)]
struct Demo {}
})
.map(|_| "Should have failed")
.unwrap_err();
assert_eq!(errors.len(), 1);
}
/// This test demonstrates flatten being used recursively.
/// Fields are expected to be consumed by the outermost matching struct.
#[test]
fn recursive_flattening() {
#[derive(FromMeta)]
struct Nested2 {
above: isize,
below: isize,
port: Option<isize>,
}
#[derive(FromMeta)]
struct Nested1 {
port: isize,
starboard: isize,
#[darling(flatten)]
z_axis: Nested2,
}
#[derive(FromMeta)]
struct Nested0 {
fore: isize,
aft: isize,
#[darling(flatten)]
cross_section: Nested1,
}
#[derive(FromDeriveInput)]
#[darling(attributes(boat))]
struct BoatPosition {
#[darling(flatten)]
pos: Nested0,
}
let parsed = BoatPosition::from_derive_input(&parse_quote! {
#[boat(fore = 1, aft = 1, port = 10, starboard = 50, above = 20, below = -3)]
struct Demo;
})
.unwrap();
assert_eq!(parsed.pos.fore, 1);
assert_eq!(parsed.pos.aft, 1);
assert_eq!(parsed.pos.cross_section.port, 10);
assert_eq!(parsed.pos.cross_section.starboard, 50);
assert_eq!(parsed.pos.cross_section.z_axis.above, 20);
assert_eq!(parsed.pos.cross_section.z_axis.below, -3);
// This should be `None` because the `port` field in `Nested1` consumed
// the field before the leftovers were passed to `Nested2::from_list`.
assert_eq!(parsed.pos.cross_section.z_axis.port, None);
}
/// This test confirms that a collection - in this case a HashMap - can
/// be used with `flatten`.
#[test]
fn flattening_into_hashmap() {
#[derive(FromDeriveInput)]
#[darling(attributes(ca))]
struct Catchall {
hello: String,
volume: usize,
#[darling(flatten)]
others: std::collections::HashMap<String, String>,
}
let parsed = Catchall::from_derive_input(&parse_quote! {
#[ca(hello = "World", volume = 10, first_name = "Alice", second_name = "Bob")]
struct Demo;
})
.unwrap();
assert_eq!(parsed.hello, "World");
assert_eq!(parsed.volume, 10);
assert_eq!(parsed.others.len(), 2);
}
#[derive(FromMeta)]
#[allow(dead_code)]
struct Person {
first: String,
last: String,
parent: Option<Box<Person>>,
}
#[derive(FromDeriveInput)]
#[darling(attributes(v))]
#[allow(dead_code)]
struct Outer {
#[darling(flatten)]
owner: Person,
#[darling(default)]
blast: bool,
}
/// This test makes sure that field names from parent structs are not inappropriately
/// offered as alternates for unknown field errors in child structs.
///
/// A naive implementation that tried to offer all the flattened fields for "did you mean"
/// could inspect all errors returned by the flattened field's `from_list` call and add the
/// parent's field names as alternates to all unknown field errors.
///
/// THIS WOULD BE INCORRECT. Those unknown field errors may have already come from
/// child fields within the flattened struct, where the parent's field names are not valid.
#[test]
fn do_not_suggest_invalid_alts() {
let errors = Outer::from_derive_input(&parse_quote! {
#[v(first = "Hello", last = "World", parent(first = "Hi", last = "Earth", blasts = "off"))]
struct Demo;
})
.map(|_| "Should have failed")
.unwrap_err()
.to_string();
assert!(
!errors.contains("`blast`"),
"Should not contain `blast`: {}",
errors
);
}
#[test]
#[cfg(feature = "suggestions")]
fn suggest_valid_parent_alts() {
let errors = Outer::from_derive_input(&parse_quote! {
#[v(first = "Hello", bladt = false, last = "World", parent(first = "Hi", last = "Earth"))]
struct Demo;
})
.map(|_| "Should have failed")
.unwrap_err()
.to_string();
assert!(
errors.contains("`blast`"),
"Should contain `blast` as did-you-mean suggestion: {}",
errors
);
}
/// Make sure that flatten works with smart pointer types, e.g. `Box`.
///
/// The generated `flatten` impl directly calls `FromMeta::from_list`
/// rather than calling `from_meta`, and the default impl of `from_list`
/// will return an unsupported format error; this test ensures that the
/// smart pointer type is properly forwarding the `from_list` call.
#[test]
fn flattening_to_box() {
#[derive(FromDeriveInput)]
#[darling(attributes(v))]
struct Example {
#[darling(flatten)]
items: Box<Vis>,
}
let when_omitted = Example::from_derive_input(&parse_quote! {
struct Demo;
})
.unwrap();
assert!(!when_omitted.items.public.is_present());
}