diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/darling/tests/defaults.rs | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/darling/tests/defaults.rs')
-rw-r--r-- | third_party/rust/darling/tests/defaults.rs | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/third_party/rust/darling/tests/defaults.rs b/third_party/rust/darling/tests/defaults.rs new file mode 100644 index 0000000000..05ab1ed1db --- /dev/null +++ b/third_party/rust/darling/tests/defaults.rs @@ -0,0 +1,189 @@ +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<String>, + 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<String>, + 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()); + } +} |