diff --git a/shared/util_macros/src/lib.rs b/shared/util_macros/src/lib.rs --- a/shared/util_macros/src/lib.rs +++ b/shared/util_macros/src/lib.rs @@ -8,7 +8,7 @@ /// totally ignores the `tag` attribute when deserializing enum variants. /// /// This derive requires two serde attributes to be present: -/// `#[serde(tag = "type", remote = "Self")]` +/// `#[serde(tag = "<>", remote = "Self")]` /// /// ### Example /// ``` diff --git a/shared/util_macros/src/tag_aware_deserialize.rs b/shared/util_macros/src/tag_aware_deserialize.rs --- a/shared/util_macros/src/tag_aware_deserialize.rs +++ b/shared/util_macros/src/tag_aware_deserialize.rs @@ -5,6 +5,14 @@ pub fn impl_tag_aware_deserialize_macro(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; + if !has_serde_remote_self(&ast.attrs) { + panic!("{} must have #[serde(remote = \"Self\")] directive", name); + } + + let Some(tag_value) = extract_tag_value(&ast.attrs) else { + panic!("{} must have #[serde(tag = \"...\")] directive", name); + }; + let gen = quote! { impl<'de> serde::de::Deserialize<'de> for #name { fn deserialize(deserializer: D) -> std::result::Result @@ -14,7 +22,7 @@ use serde::de::Error; let this = serde_json::Value::deserialize(deserializer)?; - if let Some(found_tag) = this.get("type") { + if let Some(found_tag) = this.get(#tag_value) { if found_tag == stringify!(#name) { // now we can run _original_ deserialize return match #name::deserialize(this) { @@ -39,3 +47,38 @@ }; gen.into() } + +/// Reads the `#[serde(tag = "...")]` value +fn extract_tag_value(attrs: &[Attribute]) -> Option { + let serde_attr = attrs.iter().find(|attr| attr.path.is_ident("serde"))?; + if let Ok(Meta::List(meta_list)) = serde_attr.parse_meta() { + for nested in meta_list.nested { + if let NestedMeta::Meta(Meta::NameValue(name_value)) = nested { + if name_value.path.is_ident("tag") { + if let Lit::Str(lit_str) = name_value.lit { + return Some(lit_str.value()); + } + } + } + } + } + None +} + +/// Checks for the `#[serde(remote = "Self")]` attribute +fn has_serde_remote_self(attrs: &[Attribute]) -> bool { + let Some(serde_attr) = attrs.iter().find(|attr| attr.path.is_ident("serde")) + else { + return false; + }; + if let Ok(Meta::List(meta_list)) = serde_attr.parse_meta() { + for nested in meta_list.nested { + if let NestedMeta::Meta(Meta::NameValue(name_value)) = nested { + if name_value.path.is_ident("remote") { + return matches!(name_value.lit, Lit::Str(lit_str) if lit_str.value() == "Self"); + } + } + } + } + false +}