events: Add support for type aliases
Allow to use unstable types
This commit is contained in:
parent
6a2950884d
commit
1073530ac6
@ -6,7 +6,7 @@ error: no event type attribute found, add `#[ruma_event(type = "any.room.event",
|
||||
|
|
||||
= note: this error originates in the derive macro `EventContent` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: expected one of: `type`, `kind`, `skip_redaction`, `custom_redacted`, `type_fragment`, `state_key_type`
|
||||
error: expected one of: `type`, `kind`, `skip_redaction`, `custom_redacted`, `type_fragment`, `state_key_type`, `alias`
|
||||
--> tests/events/ui/03-invalid-event-type.rs:11:14
|
||||
|
|
||||
11 | #[ruma_event(event = "m.macro.test", kind = State)]
|
||||
|
@ -4,7 +4,10 @@ use ruma_macros::event_enum;
|
||||
event_enum! {
|
||||
/// Any global account data event.
|
||||
enum GlobalAccountData {
|
||||
#[ruma_enum(alias = "io.ruma.direct")]
|
||||
"m.direct" => events::direct,
|
||||
#[ruma_enum(alias = "m.identity_server")]
|
||||
"io.ruma.identity_server" => events::identity_server,
|
||||
#[cfg(test)]
|
||||
"m.ignored_user_list" => events::ignored_user_list,
|
||||
// Doesn't actually have a wildcard, but this should work as a wildcard test
|
||||
@ -14,7 +17,24 @@ event_enum! {
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
fn main() {
|
||||
assert_eq!(GlobalAccountDataEventType::from("m.direct"), GlobalAccountDataEventType::Direct);
|
||||
assert_eq!(
|
||||
GlobalAccountDataEventType::from("io.ruma.direct"),
|
||||
GlobalAccountDataEventType::Direct
|
||||
);
|
||||
assert_eq!(GlobalAccountDataEventType::Direct.to_cow_str(), "m.direct");
|
||||
|
||||
assert_eq!(
|
||||
GlobalAccountDataEventType::from("m.identity_server"),
|
||||
GlobalAccountDataEventType::IdentityServer
|
||||
);
|
||||
assert_eq!(
|
||||
GlobalAccountDataEventType::from("io.ruma.identity_server"),
|
||||
GlobalAccountDataEventType::IdentityServer
|
||||
);
|
||||
assert_eq!(GlobalAccountDataEventType::IdentityServer.to_cow_str(), "io.ruma.identity_server");
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
|
@ -23,6 +23,8 @@ mod kw {
|
||||
syn::custom_keyword!(type_fragment);
|
||||
// The type to use for a state events' `state_key` field.
|
||||
syn::custom_keyword!(state_key_type);
|
||||
// Another type string accepted for deserialization.
|
||||
syn::custom_keyword!(alias);
|
||||
}
|
||||
|
||||
/// Parses attributes for `*EventContent` derives.
|
||||
@ -47,6 +49,9 @@ enum EventMeta {
|
||||
TypeFragment,
|
||||
|
||||
StateKeyType(Box<Type>),
|
||||
|
||||
/// Variant that holds alternate event type accepted for deserialization.
|
||||
Alias(LitStr),
|
||||
}
|
||||
|
||||
impl EventMeta {
|
||||
@ -70,6 +75,13 @@ impl EventMeta {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_alias(&self) -> Option<&LitStr> {
|
||||
match self {
|
||||
Self::Alias(t) => Some(t),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for EventMeta {
|
||||
@ -96,6 +108,10 @@ impl Parse for EventMeta {
|
||||
let _: kw::state_key_type = input.parse()?;
|
||||
let _: Token![=] = input.parse()?;
|
||||
input.parse().map(EventMeta::StateKeyType)
|
||||
} else if lookahead.peek(kw::alias) {
|
||||
let _: kw::alias = input.parse()?;
|
||||
let _: Token![=] = input.parse()?;
|
||||
input.parse().map(EventMeta::Alias)
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
@ -120,6 +136,10 @@ impl MetaAttrs {
|
||||
fn get_state_key_type(&self) -> Option<&Type> {
|
||||
self.0.iter().find_map(|a| a.get_state_key_type())
|
||||
}
|
||||
|
||||
fn get_aliases(&self) -> impl Iterator<Item = &LitStr> {
|
||||
self.0.iter().filter_map(|a| a.get_alias())
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for MetaAttrs {
|
||||
@ -227,6 +247,16 @@ pub fn expand_event_content(
|
||||
));
|
||||
}
|
||||
|
||||
let aliases: Vec<_> = content_attr.iter().flat_map(|attrs| attrs.get_aliases()).collect();
|
||||
for alias in &aliases {
|
||||
if alias.value().ends_with(".*") != prefix.is_some() {
|
||||
return Err(syn::Error::new_spanned(
|
||||
event_type,
|
||||
"aliases should have the same `.*` suffix, or lack thereof, as the main event type",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// We only generate redacted content structs for state and message-like events
|
||||
let redacted_event_content = needs_redacted(&content_attr, event_kind).then(|| {
|
||||
generate_redacted_event_content(
|
||||
@ -235,6 +265,7 @@ pub fn expand_event_content(
|
||||
event_type,
|
||||
event_kind,
|
||||
state_key_type.as_ref(),
|
||||
&aliases,
|
||||
ruma_common,
|
||||
)
|
||||
.unwrap_or_else(syn::Error::into_compile_error)
|
||||
@ -246,6 +277,7 @@ pub fn expand_event_content(
|
||||
event_type,
|
||||
event_kind,
|
||||
state_key_type.as_ref(),
|
||||
&aliases,
|
||||
ruma_common,
|
||||
)
|
||||
.unwrap_or_else(syn::Error::into_compile_error);
|
||||
@ -270,6 +302,7 @@ fn generate_redacted_event_content<'a>(
|
||||
event_type: &LitStr,
|
||||
event_kind: Option<EventKind>,
|
||||
state_key_type: Option<&TokenStream>,
|
||||
aliases: &[&LitStr],
|
||||
ruma_common: &TokenStream,
|
||||
) -> syn::Result<TokenStream> {
|
||||
assert!(
|
||||
@ -354,6 +387,7 @@ fn generate_redacted_event_content<'a>(
|
||||
event_type,
|
||||
event_kind,
|
||||
state_key_type,
|
||||
aliases,
|
||||
ruma_common,
|
||||
)
|
||||
.unwrap_or_else(syn::Error::into_compile_error);
|
||||
@ -362,6 +396,12 @@ fn generate_redacted_event_content<'a>(
|
||||
generate_static_event_content_impl(&redacted_ident, k, true, event_type, ruma_common)
|
||||
});
|
||||
|
||||
let mut event_types = aliases.to_owned();
|
||||
event_types.push(event_type);
|
||||
let event_types = quote! {
|
||||
[#(#event_types,)*]
|
||||
};
|
||||
|
||||
Ok(quote! {
|
||||
// this is the non redacted event content's impl
|
||||
#[automatically_derived]
|
||||
@ -387,9 +427,9 @@ fn generate_redacted_event_content<'a>(
|
||||
#[automatically_derived]
|
||||
impl #ruma_common::events::RedactedEventContent for #redacted_ident {
|
||||
fn empty(ev_type: &str) -> #serde_json::Result<Self> {
|
||||
if ev_type != #event_type {
|
||||
if !#event_types.contains(&ev_type) {
|
||||
return Err(#serde::de::Error::custom(
|
||||
format!("expected event type `{}`, found `{}`", #event_type, ev_type)
|
||||
format!("expected event type as one of `{:?}`, found `{}`", #event_types, ev_type)
|
||||
));
|
||||
}
|
||||
|
||||
@ -476,6 +516,7 @@ fn generate_event_content_impl<'a>(
|
||||
event_type: &LitStr,
|
||||
event_kind: Option<EventKind>,
|
||||
state_key_type: Option<&TokenStream>,
|
||||
aliases: &[&'a LitStr],
|
||||
ruma_common: &TokenStream,
|
||||
) -> syn::Result<TokenStream> {
|
||||
let serde = quote! { #ruma_common::exports::serde };
|
||||
@ -565,24 +606,41 @@ fn generate_event_content_impl<'a>(
|
||||
}
|
||||
});
|
||||
|
||||
let from_parts_fn_impl = if let Some((type_prefix, type_fragment_field)) = &type_suffix_data {
|
||||
let event_types = aliases.iter().chain([&event_type]);
|
||||
|
||||
let from_parts_fn_impl = if let Some((_, type_fragment_field)) = &type_suffix_data {
|
||||
let type_prefixes = event_types.map(|ev_type| {
|
||||
ev_type
|
||||
.value()
|
||||
.strip_suffix('*')
|
||||
.expect("aliases have already been checked to have the same suffix")
|
||||
.to_owned()
|
||||
});
|
||||
let type_prefixes = quote! {
|
||||
[#(#type_prefixes,)*]
|
||||
};
|
||||
|
||||
quote! {
|
||||
if let Some(type_fragment) = ev_type.strip_prefix(#type_prefix) {
|
||||
if let Some(type_fragment) = #type_prefixes.iter().find_map(|prefix| ev_type.strip_prefix(prefix)) {
|
||||
let mut content: Self = #serde_json::from_str(content.get())?;
|
||||
content.#type_fragment_field = type_fragment.to_owned();
|
||||
|
||||
::std::result::Result::Ok(content)
|
||||
} else {
|
||||
::std::result::Result::Err(#serde::de::Error::custom(
|
||||
::std::format!("expected event type starting with `{}`, found `{}`", #type_prefix, ev_type)
|
||||
::std::format!("expected event type starting with one of `{:?}`, found `{}`", #type_prefixes, ev_type)
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let event_types = quote! {
|
||||
[#(#event_types,)*]
|
||||
};
|
||||
|
||||
quote! {
|
||||
if ev_type != #event_type {
|
||||
if !#event_types.contains(&ev_type) {
|
||||
return ::std::result::Result::Err(#serde::de::Error::custom(
|
||||
::std::format!("expected event type `{}`, found `{}`", #event_type, ev_type)
|
||||
::std::format!("expected event type as one of `{:?}`, found `{}`", #event_types, ev_type)
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Implementation of event enum and event content enum macros.
|
||||
|
||||
use std::fmt;
|
||||
use std::{fmt, iter::zip};
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{format_ident, quote, IdentFragment, ToTokens};
|
||||
@ -49,24 +49,24 @@ pub fn expand_event_enums(input: &EventEnumDecl) -> syn::Result<TokenStream> {
|
||||
|
||||
let kind = input.kind;
|
||||
let attrs = &input.attrs;
|
||||
let events: Vec<_> =
|
||||
input.events.iter().map(|entry| (entry.ev_type.clone(), entry.ev_path.clone())).collect();
|
||||
let docs: Vec<_> = input.events.iter().map(EventEnumEntry::docs).collect::<syn::Result<_>>()?;
|
||||
let variants: Vec<_> =
|
||||
input.events.iter().map(EventEnumEntry::to_variant).collect::<syn::Result<_>>()?;
|
||||
|
||||
let events = &events;
|
||||
let events = &input.events;
|
||||
let docs = &docs;
|
||||
let variants = &variants;
|
||||
let ruma_common = &ruma_common;
|
||||
|
||||
res.extend(expand_content_enum(kind, events, attrs, variants, ruma_common));
|
||||
res.extend(expand_content_enum(kind, events, docs, attrs, variants, ruma_common));
|
||||
res.extend(
|
||||
expand_event_enum(kind, V::None, events, attrs, variants, ruma_common)
|
||||
expand_event_enum(kind, V::None, events, docs, attrs, variants, ruma_common)
|
||||
.unwrap_or_else(syn::Error::into_compile_error),
|
||||
);
|
||||
|
||||
if matches!(kind, EventKind::MessageLike | EventKind::State) {
|
||||
res.extend(
|
||||
expand_event_enum(kind, V::Sync, events, attrs, variants, ruma_common)
|
||||
expand_event_enum(kind, V::Sync, events, docs, attrs, variants, ruma_common)
|
||||
.unwrap_or_else(syn::Error::into_compile_error),
|
||||
);
|
||||
res.extend(
|
||||
@ -89,18 +89,18 @@ pub fn expand_event_enums(input: &EventEnumDecl) -> syn::Result<TokenStream> {
|
||||
|
||||
if matches!(kind, EventKind::Ephemeral) {
|
||||
res.extend(
|
||||
expand_event_enum(kind, V::Sync, events, attrs, variants, ruma_common)
|
||||
expand_event_enum(kind, V::Sync, events, docs, attrs, variants, ruma_common)
|
||||
.unwrap_or_else(syn::Error::into_compile_error),
|
||||
);
|
||||
}
|
||||
|
||||
if matches!(kind, EventKind::State) {
|
||||
res.extend(
|
||||
expand_event_enum(kind, V::Stripped, events, attrs, variants, ruma_common)
|
||||
expand_event_enum(kind, V::Stripped, events, docs, attrs, variants, ruma_common)
|
||||
.unwrap_or_else(syn::Error::into_compile_error),
|
||||
);
|
||||
res.extend(
|
||||
expand_event_enum(kind, V::Initial, events, attrs, variants, ruma_common)
|
||||
expand_event_enum(kind, V::Initial, events, docs, attrs, variants, ruma_common)
|
||||
.unwrap_or_else(syn::Error::into_compile_error),
|
||||
);
|
||||
}
|
||||
@ -111,7 +111,8 @@ pub fn expand_event_enums(input: &EventEnumDecl) -> syn::Result<TokenStream> {
|
||||
fn expand_event_enum(
|
||||
kind: EventKind,
|
||||
var: EventEnumVariation,
|
||||
events: &[(LitStr, Path)],
|
||||
events: &[EventEnumEntry],
|
||||
docs: &[TokenStream],
|
||||
attrs: &[Attribute],
|
||||
variants: &[EventEnumVariant],
|
||||
ruma_common: &TokenStream,
|
||||
@ -120,13 +121,18 @@ fn expand_event_enum(
|
||||
let ident = kind.to_event_enum_ident(var.into())?;
|
||||
|
||||
let variant_decls = variants.iter().map(|v| v.decl());
|
||||
let content: Vec<_> =
|
||||
events.iter().map(|(name, path)| to_event_path(name, path, kind, var)).collect();
|
||||
let event_types: Vec<_> = events.iter().map(|(name, _)| name).collect();
|
||||
let content: Vec<_> = events
|
||||
.iter()
|
||||
.map(|event| {
|
||||
event
|
||||
.stable_name()
|
||||
.map(|stable_name| to_event_path(stable_name, &event.ev_path, kind, var))
|
||||
})
|
||||
.collect::<syn::Result<_>>()?;
|
||||
|
||||
let custom_ty = format_ident!("Custom{}Content", kind);
|
||||
|
||||
let deserialize_impl = expand_deserialize_impl(kind, var, events, variants, ruma_common)?;
|
||||
let deserialize_impl = expand_deserialize_impl(kind, var, events, ruma_common)?;
|
||||
let field_accessor_impl = expand_accessor_methods(kind, var, variants, ruma_common)?;
|
||||
let from_impl = expand_from_impl(&ident, &content, variants);
|
||||
|
||||
@ -137,7 +143,7 @@ fn expand_event_enum(
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
pub enum #ident {
|
||||
#(
|
||||
#[doc = #event_types]
|
||||
#docs
|
||||
#variant_decls(#content),
|
||||
)*
|
||||
/// An event not defined by the Matrix specification
|
||||
@ -156,8 +162,7 @@ fn expand_event_enum(
|
||||
fn expand_deserialize_impl(
|
||||
kind: EventKind,
|
||||
var: EventEnumVariation,
|
||||
events: &[(LitStr, Path)],
|
||||
variants: &[EventEnumVariant],
|
||||
events: &[EventEnumEntry],
|
||||
ruma_common: &TokenStream,
|
||||
) -> syn::Result<TokenStream> {
|
||||
let serde = quote! { #ruma_common::exports::serde };
|
||||
@ -165,13 +170,27 @@ fn expand_deserialize_impl(
|
||||
|
||||
let ident = kind.to_event_enum_ident(var.into())?;
|
||||
|
||||
let variant_attrs = variants.iter().map(|v| {
|
||||
let attrs = &v.attrs;
|
||||
quote! { #(#attrs)* }
|
||||
});
|
||||
let self_variants = variants.iter().map(|v| v.ctor(quote! { Self }));
|
||||
let content = events.iter().map(|(name, path)| to_event_path(name, path, kind, var));
|
||||
let event_types: Vec<_> = events.iter().map(|(name, _)| name).collect();
|
||||
let match_arms: TokenStream = events
|
||||
.iter()
|
||||
.map(|event| {
|
||||
let variant = event.to_variant()?;
|
||||
let variant_attrs = {
|
||||
let attrs = &variant.attrs;
|
||||
quote! { #(#attrs)* }
|
||||
};
|
||||
let self_variant = variant.ctor(quote! { Self });
|
||||
let content = to_event_path(event.stable_name()?, &event.ev_path, kind, var);
|
||||
let ev_types = event.aliases.iter().chain([&event.ev_type]);
|
||||
|
||||
Ok(quote! {
|
||||
#variant_attrs #(#ev_types)|* => {
|
||||
let event = #serde_json::from_str::<#content>(json.get())
|
||||
.map_err(D::Error::custom)?;
|
||||
Ok(#self_variant(event))
|
||||
},
|
||||
})
|
||||
})
|
||||
.collect::<syn::Result<_>>()?;
|
||||
|
||||
Ok(quote! {
|
||||
#[allow(unused_qualifications)]
|
||||
@ -187,13 +206,7 @@ fn expand_deserialize_impl(
|
||||
#ruma_common::serde::from_raw_json_value(&json)?;
|
||||
|
||||
match &*ev_type {
|
||||
#(
|
||||
#variant_attrs #event_types => {
|
||||
let event = #serde_json::from_str::<#content>(json.get())
|
||||
.map_err(D::Error::custom)?;
|
||||
Ok(#self_variants(event))
|
||||
},
|
||||
)*
|
||||
#match_arms
|
||||
_ => {
|
||||
let event = #serde_json::from_str(json.get()).map_err(D::Error::custom)?;
|
||||
Ok(Self::_Custom(event))
|
||||
@ -293,11 +306,12 @@ fn expand_into_full_event(
|
||||
/// Create a content enum from `EventEnumInput`.
|
||||
fn expand_content_enum(
|
||||
kind: EventKind,
|
||||
events: &[(LitStr, Path)],
|
||||
events: &[EventEnumEntry],
|
||||
docs: &[TokenStream],
|
||||
attrs: &[Attribute],
|
||||
variants: &[EventEnumVariant],
|
||||
ruma_common: &TokenStream,
|
||||
) -> TokenStream {
|
||||
) -> syn::Result<TokenStream> {
|
||||
let serde = quote! { #ruma_common::exports::serde };
|
||||
let serde_json = quote! { #ruma_common::exports::serde_json };
|
||||
|
||||
@ -305,31 +319,53 @@ fn expand_content_enum(
|
||||
|
||||
let event_type_enum = kind.to_event_type_enum();
|
||||
|
||||
let content: Vec<_> =
|
||||
events.iter().map(|(name, path)| to_event_content_path(kind, name, path, None)).collect();
|
||||
let event_type_match_arms = events.iter().map(|(s, _)| {
|
||||
if let Some(prefix) = s.value().strip_suffix(".*") {
|
||||
quote! { _s if _s.starts_with(#prefix) }
|
||||
} else {
|
||||
quote! { #s }
|
||||
}
|
||||
});
|
||||
let event_types: Vec<_> = events.iter().map(|(name, _)| name).collect();
|
||||
let content: Vec<_> = events
|
||||
.iter()
|
||||
.map(|event| {
|
||||
let stable_name = event.stable_name()?;
|
||||
Ok(to_event_content_path(kind, stable_name, &event.ev_path, None))
|
||||
})
|
||||
.collect::<syn::Result<_>>()?;
|
||||
let event_type_match_arms: TokenStream = zip(zip(events, variants), &content)
|
||||
.map(|((event, variant), ev_content)| {
|
||||
let variant_attrs = {
|
||||
let attrs = &variant.attrs;
|
||||
quote! { #(#attrs)* }
|
||||
};
|
||||
let variant_ctor = variant.ctor(quote! { Self });
|
||||
|
||||
let ev_types = event.aliases.iter().chain([&event.ev_type]);
|
||||
let ev_types = if event.ev_type.value().ends_with(".*") {
|
||||
let ev_types = ev_types.map(|ev_type| {
|
||||
ev_type
|
||||
.value()
|
||||
.strip_suffix(".*")
|
||||
.expect("aliases have already been checked to have the same suffix")
|
||||
.to_owned()
|
||||
});
|
||||
quote! { _s if #(_s.starts_with(#ev_types))||* }
|
||||
} else {
|
||||
quote! { #(#ev_types)|* }
|
||||
};
|
||||
|
||||
Ok(quote! {
|
||||
#variant_attrs #ev_types => {
|
||||
let content = #ev_content::from_parts(event_type, input)?;
|
||||
::std::result::Result::Ok(#variant_ctor(content))
|
||||
},
|
||||
})
|
||||
})
|
||||
.collect::<syn::Result<_>>()?;
|
||||
|
||||
let variant_decls = variants.iter().map(|v| v.decl()).collect::<Vec<_>>();
|
||||
let variant_attrs = variants.iter().map(|v| {
|
||||
let attrs = &v.attrs;
|
||||
quote! { #(#attrs)* }
|
||||
});
|
||||
let variant_arms = variants.iter().map(|v| v.match_arm(quote! { Self })).collect::<Vec<_>>();
|
||||
let variant_ctors = variants.iter().map(|v| v.ctor(quote! { Self }));
|
||||
|
||||
let from_impl = expand_from_impl(&ident, &content, variants);
|
||||
|
||||
let serialize_custom_event_error_path =
|
||||
quote! { #ruma_common::events::serialize_custom_event_error }.to_string();
|
||||
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
#( #attrs )*
|
||||
#[derive(Clone, Debug, #serde::Serialize)]
|
||||
#[serde(untagged)]
|
||||
@ -337,7 +373,7 @@ fn expand_content_enum(
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
pub enum #ident {
|
||||
#(
|
||||
#[doc = #event_types]
|
||||
#docs
|
||||
#variant_decls(#content),
|
||||
)*
|
||||
#[doc(hidden)]
|
||||
@ -363,12 +399,7 @@ fn expand_content_enum(
|
||||
input: &#serde_json::value::RawValue,
|
||||
) -> #serde_json::Result<Self> {
|
||||
match event_type {
|
||||
#(
|
||||
#variant_attrs #event_type_match_arms => {
|
||||
let content = #content::from_parts(event_type, input)?;
|
||||
::std::result::Result::Ok(#variant_ctors(content))
|
||||
}
|
||||
)*
|
||||
#event_type_match_arms
|
||||
ty => {
|
||||
::std::result::Result::Ok(Self::_Custom {
|
||||
event_type: crate::PrivOwnedStr(ty.into()),
|
||||
@ -379,7 +410,7 @@ fn expand_content_enum(
|
||||
}
|
||||
|
||||
#from_impl
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn expand_redact(
|
||||
@ -654,10 +685,75 @@ impl EventEnumEntry {
|
||||
|
||||
pub(crate) fn to_variant(&self) -> syn::Result<EventEnumVariant> {
|
||||
let attrs = self.attrs.clone();
|
||||
let ident = m_prefix_name_to_type_name(&self.ev_type)?;
|
||||
let ident = m_prefix_name_to_type_name(self.stable_name()?)?;
|
||||
|
||||
Ok(EventEnumVariant { attrs, ident })
|
||||
}
|
||||
|
||||
pub(crate) fn stable_name(&self) -> syn::Result<&LitStr> {
|
||||
if self.ev_type.value().starts_with("m.") {
|
||||
Ok(&self.ev_type)
|
||||
} else {
|
||||
self.aliases.iter().find(|alias| alias.value().starts_with("m.")).ok_or_else(|| {
|
||||
syn::Error::new(
|
||||
Span::call_site(),
|
||||
format!(
|
||||
"A matrix event must declare a well-known type that starts with `m.` \
|
||||
either as the main type or as an alias, found `{}`",
|
||||
self.ev_type.value()
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn docs(&self) -> syn::Result<TokenStream> {
|
||||
let stable_name = self.stable_name()?;
|
||||
|
||||
let mut doc = quote! {
|
||||
#[doc = #stable_name]
|
||||
};
|
||||
|
||||
if self.ev_type != *stable_name {
|
||||
let unstable_name =
|
||||
format!("This variant uses the unstable type `{}`.", self.ev_type.value());
|
||||
|
||||
doc.extend(quote! {
|
||||
#[doc = ""]
|
||||
#[doc = #unstable_name]
|
||||
});
|
||||
}
|
||||
|
||||
match self.aliases.len() {
|
||||
0 => {}
|
||||
1 => {
|
||||
let alias = format!(
|
||||
"This variant can also be deserialized from the `{}` type.",
|
||||
self.aliases[0].value()
|
||||
);
|
||||
doc.extend(quote! {
|
||||
#[doc = ""]
|
||||
#[doc = #alias]
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
let aliases = format!(
|
||||
"This variant can also be deserialized from the following types: {}.",
|
||||
self.aliases
|
||||
.iter()
|
||||
.map(|alias| format!("`{}`", alias.value()))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
);
|
||||
doc.extend(quote! {
|
||||
#[doc = ""]
|
||||
#[doc = #aliases]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(doc)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn expand_from_impls_derived(input: DeriveInput) -> TokenStream {
|
||||
|
@ -7,6 +7,7 @@ use quote::{format_ident, IdentFragment};
|
||||
use syn::{
|
||||
braced,
|
||||
parse::{self, Parse, ParseStream},
|
||||
punctuated::Punctuated,
|
||||
Attribute, Ident, LitStr, Path, Token,
|
||||
};
|
||||
|
||||
@ -14,6 +15,7 @@ use syn::{
|
||||
mod kw {
|
||||
syn::custom_keyword!(kind);
|
||||
syn::custom_keyword!(events);
|
||||
syn::custom_keyword!(alias);
|
||||
}
|
||||
|
||||
// If the variants of this enum change `to_event_path` needs to be updated as well.
|
||||
@ -230,18 +232,41 @@ pub fn to_kind_variation(ident: &Ident) -> Option<(EventKind, EventKindVariation
|
||||
|
||||
pub struct EventEnumEntry {
|
||||
pub attrs: Vec<Attribute>,
|
||||
pub aliases: Vec<LitStr>,
|
||||
pub ev_type: LitStr,
|
||||
pub ev_path: Path,
|
||||
}
|
||||
|
||||
impl Parse for EventEnumEntry {
|
||||
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
||||
let attrs = input.call(Attribute::parse_outer)?;
|
||||
let (ruma_enum_attrs, attrs) = input
|
||||
.call(Attribute::parse_outer)?
|
||||
.into_iter()
|
||||
.partition::<Vec<_>, _>(|attr| attr.path.is_ident("ruma_enum"));
|
||||
let ev_type: LitStr = input.parse()?;
|
||||
let _: Token![=>] = input.parse()?;
|
||||
let ev_path = input.call(Path::parse_mod_style)?;
|
||||
let has_suffix = ev_type.value().ends_with(".*");
|
||||
|
||||
Ok(Self { attrs, ev_type, ev_path })
|
||||
let mut aliases = Vec::with_capacity(ruma_enum_attrs.len());
|
||||
for attr_list in ruma_enum_attrs {
|
||||
for alias_attr in attr_list
|
||||
.parse_args_with(Punctuated::<EventEnumAliasAttr, Token![,]>::parse_terminated)?
|
||||
{
|
||||
let alias = alias_attr.into_inner();
|
||||
|
||||
if alias.value().ends_with(".*") == has_suffix {
|
||||
aliases.push(alias)
|
||||
} else {
|
||||
return Err(syn::Error::new_spanned(
|
||||
&attr_list,
|
||||
"aliases should have the same `.*` suffix, or lack thereof, as the main event type",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self { attrs, aliases, ev_type, ev_path })
|
||||
}
|
||||
}
|
||||
|
||||
@ -285,3 +310,20 @@ impl Parse for EventEnumInput {
|
||||
Ok(EventEnumInput { enums })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventEnumAliasAttr(LitStr);
|
||||
|
||||
impl EventEnumAliasAttr {
|
||||
pub fn into_inner(self) -> LitStr {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for EventEnumAliasAttr {
|
||||
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
||||
let _: kw::alias = input.parse()?;
|
||||
let _: Token![=] = input.parse()?;
|
||||
let s: LitStr = input.parse()?;
|
||||
Ok(Self(s))
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ pub fn expand_event_type_enum(
|
||||
}
|
||||
let presence = vec![EventEnumEntry {
|
||||
attrs: vec![],
|
||||
aliases: vec![],
|
||||
ev_type: LitStr::new("m.presence", Span::call_site()),
|
||||
ev_path: parse_quote! { #ruma_common::events::presence },
|
||||
}];
|
||||
@ -139,28 +140,32 @@ fn generate_enum(
|
||||
})
|
||||
.collect::<syn::Result<_>>()?;
|
||||
|
||||
let from_str_match_arms: Vec<_> = deduped
|
||||
.iter()
|
||||
.map(|e| {
|
||||
let v = e.to_variant()?;
|
||||
let ctor = v.ctor(quote! { Self });
|
||||
let mut from_str_match_arms = TokenStream::new();
|
||||
for event in &deduped {
|
||||
let v = event.to_variant()?;
|
||||
let ctor = v.ctor(quote! { Self });
|
||||
let ev_types = event.aliases.iter().chain([&event.ev_type]);
|
||||
let attrs = &event.attrs;
|
||||
|
||||
let match_arm = if let Some(prefix) = e.ev_type.value().strip_suffix('*') {
|
||||
quote! {
|
||||
if event.ev_type.value().ends_with(".*") {
|
||||
for ev_type in ev_types {
|
||||
let name = ev_type.value();
|
||||
let prefix = name
|
||||
.strip_suffix('*')
|
||||
.expect("aliases have already been checked to have the same suffix");
|
||||
|
||||
from_str_match_arms.extend(quote! {
|
||||
#(#attrs)*
|
||||
// Use if-let guard once available
|
||||
_s if _s.starts_with(#prefix) => {
|
||||
#ctor(::std::convert::From::from(_s.strip_prefix(#prefix).unwrap()))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let t = &e.ev_type;
|
||||
quote! { #t => #ctor }
|
||||
};
|
||||
|
||||
let attrs = &e.attrs;
|
||||
Ok(quote! { #(#attrs)* #match_arm })
|
||||
})
|
||||
.collect::<syn::Result<_>>()?;
|
||||
})
|
||||
}
|
||||
} else {
|
||||
from_str_match_arms.extend(quote! { #(#attrs)* #(#ev_types)|* => #ctor, })
|
||||
}
|
||||
}
|
||||
|
||||
let from_ident_for_room = if ident == "StateEventType" || ident == "MessageLikeEventType" {
|
||||
let match_arms: Vec<_> = deduped
|
||||
@ -232,7 +237,7 @@ fn generate_enum(
|
||||
impl ::std::convert::From<&::std::primitive::str> for #ident {
|
||||
fn from(s: &::std::primitive::str) -> Self {
|
||||
match s {
|
||||
#(#from_str_match_arms,)*
|
||||
#from_str_match_arms
|
||||
_ => Self::_Custom(crate::PrivOwnedStr(::std::convert::From::from(s))),
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user