From af61a6220227b5ab749aa18f25657e43788bd052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= <76261501+zecakeh@users.noreply.github.com> Date: Fri, 18 Mar 2022 12:38:47 +0100 Subject: [PATCH] macros: Add alias attribute to ruma_enum --- crates/ruma-common/tests/serde/enum_derive.rs | 11 +++++ crates/ruma-macros/src/serde/attr.rs | 33 ++++++++++----- .../ruma-macros/src/serde/enum_as_ref_str.rs | 8 +++- .../ruma-macros/src/serde/enum_from_string.rs | 15 +++++-- crates/ruma-macros/src/serde/util.rs | 40 +++++++++++++------ 5 files changed, 80 insertions(+), 27 deletions(-) diff --git a/crates/ruma-common/tests/serde/enum_derive.rs b/crates/ruma-common/tests/serde/enum_derive.rs index 00d28cd3..ecbcf58b 100644 --- a/crates/ruma-common/tests/serde/enum_derive.rs +++ b/crates/ruma-common/tests/serde/enum_derive.rs @@ -12,6 +12,8 @@ enum MyEnum { #[ruma_enum(rename = "m.third")] Third, HelloWorld, + #[ruma_enum(rename = "io.ruma.unstable", alias = "m.stable", alias = "hs.notareal.unstable")] + Stable, _Custom(PrivOwnedStr), } @@ -21,6 +23,7 @@ fn as_ref_str() { assert_eq!(MyEnum::Second.as_ref(), "second"); assert_eq!(MyEnum::Third.as_ref(), "m.third"); assert_eq!(MyEnum::HelloWorld.as_ref(), "hello_world"); + assert_eq!(MyEnum::Stable.as_ref(), "io.ruma.unstable"); assert_eq!(MyEnum::_Custom(PrivOwnedStr("HelloWorld".into())).as_ref(), "HelloWorld"); } @@ -30,6 +33,7 @@ fn display() { assert_eq!(MyEnum::Second.to_string(), "second"); assert_eq!(MyEnum::Third.to_string(), "m.third"); assert_eq!(MyEnum::HelloWorld.to_string(), "hello_world"); + assert_eq!(MyEnum::Stable.to_string(), "io.ruma.unstable"); assert_eq!(MyEnum::_Custom(PrivOwnedStr("HelloWorld".into())).to_string(), "HelloWorld"); } @@ -39,6 +43,9 @@ fn from_string() { assert_eq!(MyEnum::from("second"), MyEnum::Second); assert_eq!(MyEnum::from("m.third"), MyEnum::Third); assert_eq!(MyEnum::from("hello_world"), MyEnum::HelloWorld); + assert_eq!(MyEnum::from("io.ruma.unstable"), MyEnum::Stable); + assert_eq!(MyEnum::from("m.stable"), MyEnum::Stable); + assert_eq!(MyEnum::from("hs.notareal.unstable"), MyEnum::Stable); assert_eq!(MyEnum::from("HelloWorld"), MyEnum::_Custom(PrivOwnedStr("HelloWorld".into()))); } @@ -46,6 +53,7 @@ fn from_string() { fn serialize() { assert_eq!(to_json_value(MyEnum::First).unwrap(), json!("first")); assert_eq!(to_json_value(MyEnum::HelloWorld).unwrap(), json!("hello_world")); + assert_eq!(to_json_value(MyEnum::Stable).unwrap(), json!("io.ruma.unstable")); assert_eq!( to_json_value(MyEnum::_Custom(PrivOwnedStr("\\\n\\".into()))).unwrap(), json!("\\\n\\") @@ -56,6 +64,9 @@ fn serialize() { fn deserialize() { assert_eq!(from_json_value::(json!("first")).unwrap(), MyEnum::First); assert_eq!(from_json_value::(json!("hello_world")).unwrap(), MyEnum::HelloWorld); + assert_eq!(from_json_value::(json!("io.ruma.unstable")).unwrap(), MyEnum::Stable); + assert_eq!(from_json_value::(json!("m.stable")).unwrap(), MyEnum::Stable); + assert_eq!(from_json_value::(json!("hs.notareal.unstable")).unwrap(), MyEnum::Stable); assert_eq!( from_json_value::(json!("\\\n\\")).unwrap(), MyEnum::_Custom(PrivOwnedStr("\\\n\\".into())) diff --git a/crates/ruma-macros/src/serde/attr.rs b/crates/ruma-macros/src/serde/attr.rs index a705d751..4fefe7b4 100644 --- a/crates/ruma-macros/src/serde/attr.rs +++ b/crates/ruma-macros/src/serde/attr.rs @@ -6,23 +6,36 @@ use syn::{ use super::case::RenameRule; mod kw { + syn::custom_keyword!(alias); syn::custom_keyword!(rename); syn::custom_keyword!(rename_all); } -pub struct RenameAttr(LitStr); - -impl RenameAttr { - pub fn into_inner(self) -> LitStr { - self.0 - } +#[derive(Default)] +pub struct EnumAttrs { + pub rename: Option, + pub aliases: Vec, } -impl Parse for RenameAttr { +pub enum Attr { + Alias(LitStr), + Rename(LitStr), +} + +impl Parse for Attr { fn parse(input: ParseStream<'_>) -> syn::Result { - let _: kw::rename = input.parse()?; - let _: Token![=] = input.parse()?; - Ok(Self(input.parse()?)) + let lookahead = input.lookahead1(); + if lookahead.peek(kw::alias) { + let _: kw::alias = input.parse()?; + let _: Token![=] = input.parse()?; + Ok(Self::Alias(input.parse()?)) + } else if lookahead.peek(kw::rename) { + let _: kw::rename = input.parse()?; + let _: Token![=] = input.parse()?; + Ok(Self::Rename(input.parse()?)) + } else { + Err(lookahead.error()) + } } } diff --git a/crates/ruma-macros/src/serde/enum_as_ref_str.rs b/crates/ruma-macros/src/serde/enum_as_ref_str.rs index 67e992bc..b4f2c72b 100644 --- a/crates/ruma-macros/src/serde/enum_as_ref_str.rs +++ b/crates/ruma-macros/src/serde/enum_as_ref_str.rs @@ -2,7 +2,10 @@ use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{Fields, FieldsNamed, FieldsUnnamed, ItemEnum}; -use super::util::{get_rename, get_rename_rule}; +use super::{ + attr::EnumAttrs, + util::{get_enum_attributes, get_rename_rule}, +}; pub fn expand_enum_as_ref_str(input: &ItemEnum) -> syn::Result { let enum_name = &input.ident; @@ -12,7 +15,8 @@ pub fn expand_enum_as_ref_str(input: &ItemEnum) -> syn::Result { .iter() .map(|v| { let variant_name = &v.ident; - let (field_capture, variant_str) = match (get_rename(v)?, &v.fields) { + let EnumAttrs { rename, .. } = get_enum_attributes(v)?; + let (field_capture, variant_str) = match (rename, &v.fields) { (None, Fields::Unit) => ( None, rename_rule.apply_to_variant(&variant_name.to_string()).into_token_stream(), diff --git a/crates/ruma-macros/src/serde/enum_from_string.rs b/crates/ruma-macros/src/serde/enum_from_string.rs index b396da0b..f2b70086 100644 --- a/crates/ruma-macros/src/serde/enum_from_string.rs +++ b/crates/ruma-macros/src/serde/enum_from_string.rs @@ -2,7 +2,10 @@ use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; use syn::{Fields, FieldsNamed, FieldsUnnamed, ItemEnum}; -use super::util::{get_rename, get_rename_rule}; +use super::{ + attr::EnumAttrs, + util::{get_enum_attributes, get_rename_rule}, +}; pub fn expand_enum_from_string(input: &ItemEnum) -> syn::Result { let enum_name = &input.ident; @@ -13,7 +16,8 @@ pub fn expand_enum_from_string(input: &ItemEnum) -> syn::Result { .iter() .map(|v| { let variant_name = &v.ident; - let variant_str = match (get_rename(v)?, &v.fields) { + let EnumAttrs { rename, aliases } = get_enum_attributes(v)?; + let variant_str = match (rename, &v.fields) { (None, Fields::Unit) => Some( rename_rule.apply_to_variant(&variant_name.to_string()).into_token_stream(), ), @@ -56,7 +60,12 @@ pub fn expand_enum_from_string(input: &ItemEnum) -> syn::Result { } }; - Ok(variant_str.map(|s| quote! { #s => #enum_name :: #variant_name })) + Ok(variant_str.map(|s| { + quote! { + #( #aliases => #enum_name :: #variant_name, )* + #s => #enum_name :: #variant_name + } + })) }) .collect::>()?; diff --git a/crates/ruma-macros/src/serde/util.rs b/crates/ruma-macros/src/serde/util.rs index dd3e0017..e1c4f907 100644 --- a/crates/ruma-macros/src/serde/util.rs +++ b/crates/ruma-macros/src/serde/util.rs @@ -1,8 +1,8 @@ use proc_macro2::Span; -use syn::{ItemEnum, LitStr, Variant}; +use syn::{punctuated::Punctuated, ItemEnum, Token, Variant}; use super::{ - attr::{RenameAllAttr, RenameAttr}, + attr::{Attr, EnumAttrs, RenameAllAttr}, case::RenameRule, }; @@ -24,16 +24,32 @@ pub fn get_rename_rule(input: &ItemEnum) -> syn::Result { } } -pub fn get_rename(input: &Variant) -> syn::Result> { - let renames: Vec<_> = input - .attrs - .iter() - .filter(|attr| attr.path.is_ident("ruma_enum")) - .map(|attr| attr.parse_args::().map(RenameAttr::into_inner)) - .collect::>()?; +pub fn get_enum_attributes(input: &Variant) -> syn::Result { + let mut attributes = EnumAttrs::default(); - match renames.len() { - 0 | 1 => Ok(renames.into_iter().next()), - _ => Err(syn::Error::new(Span::call_site(), "found multiple ruma_enum(rename) attributes")), + for attr in &input.attrs { + if !attr.path.is_ident("ruma_enum") { + continue; + } + + let enum_attrs = attr.parse_args_with(Punctuated::<_, Token![,]>::parse_terminated)?; + for enum_attr in enum_attrs { + match enum_attr { + Attr::Rename(s) => { + if attributes.rename.is_some() { + return Err(syn::Error::new( + Span::call_site(), + "found multiple ruma_enum(rename) attributes", + )); + } + attributes.rename = Some(s); + } + Attr::Alias(s) => { + attributes.aliases.push(s); + } + } + } } + + Ok(attributes) }