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")]
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::<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!("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!(
from_json_value::<MyEnum>(json!("\\\n\\")).unwrap(),
MyEnum::_Custom(PrivOwnedStr("\\\n\\".into()))

View File

@ -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<LitStr>,
pub aliases: Vec<LitStr>,
}
impl Parse for RenameAttr {
pub enum Attr {
Alias(LitStr),
Rename(LitStr),
}
impl Parse for Attr {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
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())
}
}
}

View File

@ -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<TokenStream> {
let enum_name = &input.ident;
@ -12,7 +15,8 @@ pub fn expand_enum_as_ref_str(input: &ItemEnum) -> syn::Result<TokenStream> {
.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(),

View File

@ -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<TokenStream> {
let enum_name = &input.ident;
@ -13,7 +16,8 @@ pub fn expand_enum_from_string(input: &ItemEnum) -> syn::Result<TokenStream> {
.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<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<_>>()?;

View File

@ -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<RenameRule> {
}
}
pub fn get_rename(input: &Variant) -> syn::Result<Option<LitStr>> {
let renames: Vec<_> = input
.attrs
.iter()
.filter(|attr| attr.path.is_ident("ruma_enum"))
.map(|attr| attr.parse_args::<RenameAttr>().map(RenameAttr::into_inner))
.collect::<syn::Result<_>>()?;
pub fn get_enum_attributes(input: &Variant) -> syn::Result<EnumAttrs> {
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)
}