Merge maps when combining options

This commit is contained in:
Charlie Marsh 2024-02-10 22:25:49 -05:00 committed by Micha Reiser
parent ab9b050ecd
commit 4433262627
No known key found for this signature in database
1 changed files with 45 additions and 5 deletions

View File

@ -48,13 +48,53 @@ fn handle_field(field: &Field) -> syn::Result<proc_macro2::TokenStream> {
..
}) => match segments.first() {
Some(PathSegment {
ident: type_ident, ..
}) if type_ident == "Option" => Ok(quote_spanned!(
ident.span() => #ident: self.#ident.or(other.#ident)
)),
ident: type_ident,
arguments,
}) if type_ident == "Option" => {
// Given `Option<FxHashMap<_>>`, combine the maps by merging. In TOML, a hash map is
// represented as a table, so merging the maps is the correct behavior.
if let syn::PathArguments::AngleBracketed(args) = arguments {
let inner_type_ident = args
.args
.first()
.and_then(|arg| match arg {
syn::GenericArgument::Type(Type::Path(TypePath {
path: Path { segments, .. },
..
})) => segments.first().map(|seg| &seg.ident),
_ => None,
})
.ok_or_else(|| {
syn::Error::new(
ident.span(),
"Expected `Option<_>` with a single type argument.",
)
})?;
if inner_type_ident == "HashMap"
|| inner_type_ident == "BTreeMap"
|| inner_type_ident == "FxHashMap"
{
return Ok(quote_spanned!(
ident.span() => #ident: match (self.#ident, other.#ident) {
(Some(mut m1), Some(m2)) => {
m1.extend(m2);
Some(m1)
},
(None, Some(m)) | (Some(m), None) => Some(m),
(None, None) => None,
}
));
}
}
Ok(quote_spanned!(
ident.span() => #ident: self.#ident.or(other.#ident)
))
}
_ => Err(syn::Error::new(
ident.span(),
"Expected `Option<_>` or `Vec<_>` as type.",
"Expected `Option<_>` as type.",
)),
},
_ => Err(syn::Error::new(ident.span(), "Expected type.")),