macros: Add alias attribute to ruma_enum

This commit is contained in:
Kévin Commaille 2022-03-18 12:38:47 +01:00 committed by GitHub
parent 9b870cd9af
commit af61a62202
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 80 additions and 27 deletions

View File

@ -12,6 +12,8 @@ enum MyEnum {
#[ruma_enum(rename = "m.third")] #[ruma_enum(rename = "m.third")]
Third, Third,
HelloWorld, HelloWorld,
#[ruma_enum(rename = "io.ruma.unstable", alias = "m.stable", alias = "hs.notareal.unstable")]
Stable,
_Custom(PrivOwnedStr), _Custom(PrivOwnedStr),
} }
@ -21,6 +23,7 @@ fn as_ref_str() {
assert_eq!(MyEnum::Second.as_ref(), "second"); assert_eq!(MyEnum::Second.as_ref(), "second");
assert_eq!(MyEnum::Third.as_ref(), "m.third"); assert_eq!(MyEnum::Third.as_ref(), "m.third");
assert_eq!(MyEnum::HelloWorld.as_ref(), "hello_world"); 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"); 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::Second.to_string(), "second");
assert_eq!(MyEnum::Third.to_string(), "m.third"); assert_eq!(MyEnum::Third.to_string(), "m.third");
assert_eq!(MyEnum::HelloWorld.to_string(), "hello_world"); 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"); 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("second"), MyEnum::Second);
assert_eq!(MyEnum::from("m.third"), MyEnum::Third); assert_eq!(MyEnum::from("m.third"), MyEnum::Third);
assert_eq!(MyEnum::from("hello_world"), MyEnum::HelloWorld); 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()))); assert_eq!(MyEnum::from("HelloWorld"), MyEnum::_Custom(PrivOwnedStr("HelloWorld".into())));
} }
@ -46,6 +53,7 @@ fn from_string() {
fn serialize() { fn serialize() {
assert_eq!(to_json_value(MyEnum::First).unwrap(), json!("first")); 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::HelloWorld).unwrap(), json!("hello_world"));
assert_eq!(to_json_value(MyEnum::Stable).unwrap(), json!("io.ruma.unstable"));
assert_eq!( assert_eq!(
to_json_value(MyEnum::_Custom(PrivOwnedStr("\\\n\\".into()))).unwrap(), to_json_value(MyEnum::_Custom(PrivOwnedStr("\\\n\\".into()))).unwrap(),
json!("\\\n\\") json!("\\\n\\")
@ -56,6 +64,9 @@ fn serialize() {
fn deserialize() { fn deserialize() {
assert_eq!(from_json_value::<MyEnum>(json!("first")).unwrap(), MyEnum::First); assert_eq!(from_json_value::<MyEnum>(json!("first")).unwrap(), MyEnum::First);
assert_eq!(from_json_value::<MyEnum>(json!("hello_world")).unwrap(), MyEnum::HelloWorld); assert_eq!(from_json_value::<MyEnum>(json!("hello_world")).unwrap(), MyEnum::HelloWorld);
assert_eq!(from_json_value::<MyEnum>(json!("io.ruma.unstable")).unwrap(), MyEnum::Stable);
assert_eq!(from_json_value::<MyEnum>(json!("m.stable")).unwrap(), MyEnum::Stable);
assert_eq!(from_json_value::<MyEnum>(json!("hs.notareal.unstable")).unwrap(), MyEnum::Stable);
assert_eq!( assert_eq!(
from_json_value::<MyEnum>(json!("\\\n\\")).unwrap(), from_json_value::<MyEnum>(json!("\\\n\\")).unwrap(),
MyEnum::_Custom(PrivOwnedStr("\\\n\\".into())) MyEnum::_Custom(PrivOwnedStr("\\\n\\".into()))

View File

@ -6,23 +6,36 @@ use syn::{
use super::case::RenameRule; use super::case::RenameRule;
mod kw { mod kw {
syn::custom_keyword!(alias);
syn::custom_keyword!(rename); syn::custom_keyword!(rename);
syn::custom_keyword!(rename_all); syn::custom_keyword!(rename_all);
} }
pub struct RenameAttr(LitStr); #[derive(Default)]
pub struct EnumAttrs {
impl RenameAttr { pub rename: Option<LitStr>,
pub fn into_inner(self) -> LitStr { pub aliases: Vec<LitStr>,
self.0
}
} }
impl Parse for RenameAttr { pub enum Attr {
Alias(LitStr),
Rename(LitStr),
}
impl Parse for Attr {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> { fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
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 _: kw::rename = input.parse()?;
let _: Token![=] = input.parse()?; let _: Token![=] = input.parse()?;
Ok(Self(input.parse()?)) Ok(Self::Rename(input.parse()?))
} else {
Err(lookahead.error())
}
} }
} }

View File

@ -2,7 +2,10 @@ use proc_macro2::TokenStream;
use quote::{quote, ToTokens}; use quote::{quote, ToTokens};
use syn::{Fields, FieldsNamed, FieldsUnnamed, ItemEnum}; 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<TokenStream> { pub fn expand_enum_as_ref_str(input: &ItemEnum) -> syn::Result<TokenStream> {
let enum_name = &input.ident; let enum_name = &input.ident;
@ -12,7 +15,8 @@ pub fn expand_enum_as_ref_str(input: &ItemEnum) -> syn::Result<TokenStream> {
.iter() .iter()
.map(|v| { .map(|v| {
let variant_name = &v.ident; 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, Fields::Unit) => (
None, None,
rename_rule.apply_to_variant(&variant_name.to_string()).into_token_stream(), rename_rule.apply_to_variant(&variant_name.to_string()).into_token_stream(),

View File

@ -2,7 +2,10 @@ use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens}; use quote::{quote, ToTokens};
use syn::{Fields, FieldsNamed, FieldsUnnamed, ItemEnum}; 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<TokenStream> { pub fn expand_enum_from_string(input: &ItemEnum) -> syn::Result<TokenStream> {
let enum_name = &input.ident; let enum_name = &input.ident;
@ -13,7 +16,8 @@ pub fn expand_enum_from_string(input: &ItemEnum) -> syn::Result<TokenStream> {
.iter() .iter()
.map(|v| { .map(|v| {
let variant_name = &v.ident; 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( (None, Fields::Unit) => Some(
rename_rule.apply_to_variant(&variant_name.to_string()).into_token_stream(), 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<TokenStream> {
} }
}; };
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::<syn::Result<_>>()?; .collect::<syn::Result<_>>()?;

View File

@ -1,8 +1,8 @@
use proc_macro2::Span; use proc_macro2::Span;
use syn::{ItemEnum, LitStr, Variant}; use syn::{punctuated::Punctuated, ItemEnum, Token, Variant};
use super::{ use super::{
attr::{RenameAllAttr, RenameAttr}, attr::{Attr, EnumAttrs, RenameAllAttr},
case::RenameRule, case::RenameRule,
}; };
@ -24,16 +24,32 @@ pub fn get_rename_rule(input: &ItemEnum) -> syn::Result<RenameRule> {
} }
} }
pub fn get_rename(input: &Variant) -> syn::Result<Option<LitStr>> { pub fn get_enum_attributes(input: &Variant) -> syn::Result<EnumAttrs> {
let renames: Vec<_> = input let mut attributes = EnumAttrs::default();
.attrs
.iter()
.filter(|attr| attr.path.is_ident("ruma_enum"))
.map(|attr| attr.parse_args::<RenameAttr>().map(RenameAttr::into_inner))
.collect::<syn::Result<_>>()?;
match renames.len() { for attr in &input.attrs {
0 | 1 => Ok(renames.into_iter().next()), if !attr.path.is_ident("ruma_enum") {
_ => Err(syn::Error::new(Span::call_site(), "found multiple ruma_enum(rename) attributes")), 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)
}