use darling::FromDeriveInput; use syn::parse_quote; mod foo { pub mod bar { pub fn init() -> String { String::from("hello") } } } #[derive(FromDeriveInput)] #[darling(attributes(speak))] pub struct SpeakerOpts { #[darling(default = foo::bar::init)] first_word: String, } #[test] fn path_default() { let speaker: SpeakerOpts = FromDeriveInput::from_derive_input(&parse_quote! { struct Foo; }) .expect("Unit struct with no attrs should parse"); assert_eq!(speaker.first_word, "hello"); } /// Tests in this module capture the somewhat-confusing behavior observed when defaults /// are set at both the field and container level. /// /// The general rule is that more-specific declarations preempt less-specific ones; this is /// unsurprising and allows for granular control over what happens when parsing an AST. mod stacked_defaults { use darling::{FromDeriveInput, FromMeta}; use syn::parse_quote; fn jane() -> String { "Jane".into() } #[derive(FromMeta)] #[darling(default)] struct PersonName { #[darling(default = "jane")] first: String, #[darling(default)] middle: String, last: String, } impl Default for PersonName { fn default() -> Self { Self { first: "John".into(), middle: "T".into(), last: "Doe".into(), } } } #[derive(FromDeriveInput)] #[darling(attributes(person))] struct Person { #[darling(default)] name: PersonName, age: u8, } #[test] fn name_first_only() { let person = Person::from_derive_input(&parse_quote! { #[person(name(first = "Bill"), age = 5)] struct Foo; }) .unwrap(); assert_eq!(person.name.first, "Bill"); assert_eq!( person.name.middle, "", "Explicit field-level default should preempt container-level default" ); assert_eq!( person.name.last, "Doe", "Absence of a field-level default falls back to container-level default" ); } /// This is the most surprising case. The presence of `name()` means we invoke /// `PersonName::from_list(&[])`. When that finishes parsing each of the zero nested /// items it has received, it will then start filling in missing fields, using the /// explicit field-level defaults for `first` and `middle`, while for `last` it will /// use the `last` field from the container-level default. #[test] fn name_empty_list() { let person = Person::from_derive_input(&parse_quote! { #[person(name(), age = 5)] struct Foo; }) .unwrap(); assert_eq!(person.name.first, "Jane"); assert_eq!(person.name.middle, ""); assert_eq!(person.name.last, "Doe"); } #[test] fn no_name() { let person = Person::from_derive_input(&parse_quote! { #[person(age = 5)] struct Foo; }) .unwrap(); assert_eq!(person.age, 5); assert_eq!( person.name.first, "John", "If `name` is not specified, `Person`'s field-level default should be used" ); assert_eq!(person.name.middle, "T"); assert_eq!(person.name.last, "Doe"); } } mod implicit_default { use darling::{util::Flag, FromDeriveInput}; use syn::parse_quote; // No use of `darling(default)` here at all! // This struct will fill in missing fields using FromMeta::from_none. #[derive(FromDeriveInput)] #[darling(attributes(person))] struct Person { first_name: String, last_name: Option, lefty: Flag, } #[test] fn missing_fields_fill() { let person = Person::from_derive_input(&parse_quote! { #[person(first_name = "James")] struct Foo; }) .unwrap(); assert_eq!(person.first_name, "James"); assert_eq!(person.last_name, None); assert!(!person.lefty.is_present()); } } /// Test that a field-level implicit default using FromMeta::from_none is superseded /// by the parent declaring `#[darling(default)]`. mod overridden_implicit_default { use darling::{util::Flag, FromDeriveInput}; use syn::parse_quote; #[derive(FromDeriveInput)] #[darling(default, attributes(person))] struct Person { first_name: String, last_name: Option, lefty: Flag, } impl Default for Person { fn default() -> Self { Self { first_name: "Jane".into(), last_name: Some("Doe".into()), lefty: Flag::default(), } } } #[test] fn fill_missing() { let person = Person::from_derive_input(&parse_quote!( #[person(last_name = "Archer")] struct Foo; )) .unwrap(); assert_eq!(person.first_name, "Jane"); assert_eq!(person.last_name, Some("Archer".into())); assert!(!person.lefty.is_present()); } }