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)
|
= 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
|
--> tests/events/ui/03-invalid-event-type.rs:11:14
|
||||||
|
|
|
|
||||||
11 | #[ruma_event(event = "m.macro.test", kind = State)]
|
11 | #[ruma_event(event = "m.macro.test", kind = State)]
|
||||||
|
@ -4,7 +4,10 @@ use ruma_macros::event_enum;
|
|||||||
event_enum! {
|
event_enum! {
|
||||||
/// Any global account data event.
|
/// Any global account data event.
|
||||||
enum GlobalAccountData {
|
enum GlobalAccountData {
|
||||||
|
#[ruma_enum(alias = "io.ruma.direct")]
|
||||||
"m.direct" => events::direct,
|
"m.direct" => events::direct,
|
||||||
|
#[ruma_enum(alias = "m.identity_server")]
|
||||||
|
"io.ruma.identity_server" => events::identity_server,
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
"m.ignored_user_list" => events::ignored_user_list,
|
"m.ignored_user_list" => events::ignored_user_list,
|
||||||
// Doesn't actually have a wildcard, but this should work as a wildcard test
|
// 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)]
|
#[doc(hidden)]
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
@ -23,6 +23,8 @@ mod kw {
|
|||||||
syn::custom_keyword!(type_fragment);
|
syn::custom_keyword!(type_fragment);
|
||||||
// The type to use for a state events' `state_key` field.
|
// The type to use for a state events' `state_key` field.
|
||||||
syn::custom_keyword!(state_key_type);
|
syn::custom_keyword!(state_key_type);
|
||||||
|
// Another type string accepted for deserialization.
|
||||||
|
syn::custom_keyword!(alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses attributes for `*EventContent` derives.
|
/// Parses attributes for `*EventContent` derives.
|
||||||
@ -47,6 +49,9 @@ enum EventMeta {
|
|||||||
TypeFragment,
|
TypeFragment,
|
||||||
|
|
||||||
StateKeyType(Box<Type>),
|
StateKeyType(Box<Type>),
|
||||||
|
|
||||||
|
/// Variant that holds alternate event type accepted for deserialization.
|
||||||
|
Alias(LitStr),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventMeta {
|
impl EventMeta {
|
||||||
@ -70,6 +75,13 @@ impl EventMeta {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_alias(&self) -> Option<&LitStr> {
|
||||||
|
match self {
|
||||||
|
Self::Alias(t) => Some(t),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for EventMeta {
|
impl Parse for EventMeta {
|
||||||
@ -96,6 +108,10 @@ impl Parse for EventMeta {
|
|||||||
let _: kw::state_key_type = input.parse()?;
|
let _: kw::state_key_type = input.parse()?;
|
||||||
let _: Token![=] = input.parse()?;
|
let _: Token![=] = input.parse()?;
|
||||||
input.parse().map(EventMeta::StateKeyType)
|
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 {
|
} else {
|
||||||
Err(lookahead.error())
|
Err(lookahead.error())
|
||||||
}
|
}
|
||||||
@ -120,6 +136,10 @@ impl MetaAttrs {
|
|||||||
fn get_state_key_type(&self) -> Option<&Type> {
|
fn get_state_key_type(&self) -> Option<&Type> {
|
||||||
self.0.iter().find_map(|a| a.get_state_key_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 {
|
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
|
// We only generate redacted content structs for state and message-like events
|
||||||
let redacted_event_content = needs_redacted(&content_attr, event_kind).then(|| {
|
let redacted_event_content = needs_redacted(&content_attr, event_kind).then(|| {
|
||||||
generate_redacted_event_content(
|
generate_redacted_event_content(
|
||||||
@ -235,6 +265,7 @@ pub fn expand_event_content(
|
|||||||
event_type,
|
event_type,
|
||||||
event_kind,
|
event_kind,
|
||||||
state_key_type.as_ref(),
|
state_key_type.as_ref(),
|
||||||
|
&aliases,
|
||||||
ruma_common,
|
ruma_common,
|
||||||
)
|
)
|
||||||
.unwrap_or_else(syn::Error::into_compile_error)
|
.unwrap_or_else(syn::Error::into_compile_error)
|
||||||
@ -246,6 +277,7 @@ pub fn expand_event_content(
|
|||||||
event_type,
|
event_type,
|
||||||
event_kind,
|
event_kind,
|
||||||
state_key_type.as_ref(),
|
state_key_type.as_ref(),
|
||||||
|
&aliases,
|
||||||
ruma_common,
|
ruma_common,
|
||||||
)
|
)
|
||||||
.unwrap_or_else(syn::Error::into_compile_error);
|
.unwrap_or_else(syn::Error::into_compile_error);
|
||||||
@ -270,6 +302,7 @@ fn generate_redacted_event_content<'a>(
|
|||||||
event_type: &LitStr,
|
event_type: &LitStr,
|
||||||
event_kind: Option<EventKind>,
|
event_kind: Option<EventKind>,
|
||||||
state_key_type: Option<&TokenStream>,
|
state_key_type: Option<&TokenStream>,
|
||||||
|
aliases: &[&LitStr],
|
||||||
ruma_common: &TokenStream,
|
ruma_common: &TokenStream,
|
||||||
) -> syn::Result<TokenStream> {
|
) -> syn::Result<TokenStream> {
|
||||||
assert!(
|
assert!(
|
||||||
@ -354,6 +387,7 @@ fn generate_redacted_event_content<'a>(
|
|||||||
event_type,
|
event_type,
|
||||||
event_kind,
|
event_kind,
|
||||||
state_key_type,
|
state_key_type,
|
||||||
|
aliases,
|
||||||
ruma_common,
|
ruma_common,
|
||||||
)
|
)
|
||||||
.unwrap_or_else(syn::Error::into_compile_error);
|
.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)
|
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! {
|
Ok(quote! {
|
||||||
// this is the non redacted event content's impl
|
// this is the non redacted event content's impl
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
@ -387,9 +427,9 @@ fn generate_redacted_event_content<'a>(
|
|||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
impl #ruma_common::events::RedactedEventContent for #redacted_ident {
|
impl #ruma_common::events::RedactedEventContent for #redacted_ident {
|
||||||
fn empty(ev_type: &str) -> #serde_json::Result<Self> {
|
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(
|
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_type: &LitStr,
|
||||||
event_kind: Option<EventKind>,
|
event_kind: Option<EventKind>,
|
||||||
state_key_type: Option<&TokenStream>,
|
state_key_type: Option<&TokenStream>,
|
||||||
|
aliases: &[&'a LitStr],
|
||||||
ruma_common: &TokenStream,
|
ruma_common: &TokenStream,
|
||||||
) -> syn::Result<TokenStream> {
|
) -> syn::Result<TokenStream> {
|
||||||
let serde = quote! { #ruma_common::exports::serde };
|
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! {
|
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())?;
|
let mut content: Self = #serde_json::from_str(content.get())?;
|
||||||
content.#type_fragment_field = type_fragment.to_owned();
|
content.#type_fragment_field = type_fragment.to_owned();
|
||||||
|
|
||||||
::std::result::Result::Ok(content)
|
::std::result::Result::Ok(content)
|
||||||
} else {
|
} else {
|
||||||
::std::result::Result::Err(#serde::de::Error::custom(
|
::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 {
|
} else {
|
||||||
|
let event_types = quote! {
|
||||||
|
[#(#event_types,)*]
|
||||||
|
};
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
if ev_type != #event_type {
|
if !#event_types.contains(&ev_type) {
|
||||||
return ::std::result::Result::Err(#serde::de::Error::custom(
|
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.
|
//! Implementation of event enum and event content enum macros.
|
||||||
|
|
||||||
use std::fmt;
|
use std::{fmt, iter::zip};
|
||||||
|
|
||||||
use proc_macro2::{Span, TokenStream};
|
use proc_macro2::{Span, TokenStream};
|
||||||
use quote::{format_ident, quote, IdentFragment, ToTokens};
|
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 kind = input.kind;
|
||||||
let attrs = &input.attrs;
|
let attrs = &input.attrs;
|
||||||
let events: Vec<_> =
|
let docs: Vec<_> = input.events.iter().map(EventEnumEntry::docs).collect::<syn::Result<_>>()?;
|
||||||
input.events.iter().map(|entry| (entry.ev_type.clone(), entry.ev_path.clone())).collect();
|
|
||||||
let variants: Vec<_> =
|
let variants: Vec<_> =
|
||||||
input.events.iter().map(EventEnumEntry::to_variant).collect::<syn::Result<_>>()?;
|
input.events.iter().map(EventEnumEntry::to_variant).collect::<syn::Result<_>>()?;
|
||||||
|
|
||||||
let events = &events;
|
let events = &input.events;
|
||||||
|
let docs = &docs;
|
||||||
let variants = &variants;
|
let variants = &variants;
|
||||||
let ruma_common = &ruma_common;
|
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(
|
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),
|
.unwrap_or_else(syn::Error::into_compile_error),
|
||||||
);
|
);
|
||||||
|
|
||||||
if matches!(kind, EventKind::MessageLike | EventKind::State) {
|
if matches!(kind, EventKind::MessageLike | EventKind::State) {
|
||||||
res.extend(
|
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),
|
.unwrap_or_else(syn::Error::into_compile_error),
|
||||||
);
|
);
|
||||||
res.extend(
|
res.extend(
|
||||||
@ -89,18 +89,18 @@ pub fn expand_event_enums(input: &EventEnumDecl) -> syn::Result<TokenStream> {
|
|||||||
|
|
||||||
if matches!(kind, EventKind::Ephemeral) {
|
if matches!(kind, EventKind::Ephemeral) {
|
||||||
res.extend(
|
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),
|
.unwrap_or_else(syn::Error::into_compile_error),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if matches!(kind, EventKind::State) {
|
if matches!(kind, EventKind::State) {
|
||||||
res.extend(
|
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),
|
.unwrap_or_else(syn::Error::into_compile_error),
|
||||||
);
|
);
|
||||||
res.extend(
|
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),
|
.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(
|
fn expand_event_enum(
|
||||||
kind: EventKind,
|
kind: EventKind,
|
||||||
var: EventEnumVariation,
|
var: EventEnumVariation,
|
||||||
events: &[(LitStr, Path)],
|
events: &[EventEnumEntry],
|
||||||
|
docs: &[TokenStream],
|
||||||
attrs: &[Attribute],
|
attrs: &[Attribute],
|
||||||
variants: &[EventEnumVariant],
|
variants: &[EventEnumVariant],
|
||||||
ruma_common: &TokenStream,
|
ruma_common: &TokenStream,
|
||||||
@ -120,13 +121,18 @@ fn expand_event_enum(
|
|||||||
let ident = kind.to_event_enum_ident(var.into())?;
|
let ident = kind.to_event_enum_ident(var.into())?;
|
||||||
|
|
||||||
let variant_decls = variants.iter().map(|v| v.decl());
|
let variant_decls = variants.iter().map(|v| v.decl());
|
||||||
let content: Vec<_> =
|
let content: Vec<_> = events
|
||||||
events.iter().map(|(name, path)| to_event_path(name, path, kind, var)).collect();
|
.iter()
|
||||||
let event_types: Vec<_> = events.iter().map(|(name, _)| name).collect();
|
.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 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 field_accessor_impl = expand_accessor_methods(kind, var, variants, ruma_common)?;
|
||||||
let from_impl = expand_from_impl(&ident, &content, variants);
|
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)]
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
pub enum #ident {
|
pub enum #ident {
|
||||||
#(
|
#(
|
||||||
#[doc = #event_types]
|
#docs
|
||||||
#variant_decls(#content),
|
#variant_decls(#content),
|
||||||
)*
|
)*
|
||||||
/// An event not defined by the Matrix specification
|
/// An event not defined by the Matrix specification
|
||||||
@ -156,8 +162,7 @@ fn expand_event_enum(
|
|||||||
fn expand_deserialize_impl(
|
fn expand_deserialize_impl(
|
||||||
kind: EventKind,
|
kind: EventKind,
|
||||||
var: EventEnumVariation,
|
var: EventEnumVariation,
|
||||||
events: &[(LitStr, Path)],
|
events: &[EventEnumEntry],
|
||||||
variants: &[EventEnumVariant],
|
|
||||||
ruma_common: &TokenStream,
|
ruma_common: &TokenStream,
|
||||||
) -> syn::Result<TokenStream> {
|
) -> syn::Result<TokenStream> {
|
||||||
let serde = quote! { #ruma_common::exports::serde };
|
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 ident = kind.to_event_enum_ident(var.into())?;
|
||||||
|
|
||||||
let variant_attrs = variants.iter().map(|v| {
|
let match_arms: TokenStream = events
|
||||||
let attrs = &v.attrs;
|
.iter()
|
||||||
|
.map(|event| {
|
||||||
|
let variant = event.to_variant()?;
|
||||||
|
let variant_attrs = {
|
||||||
|
let attrs = &variant.attrs;
|
||||||
quote! { #(#attrs)* }
|
quote! { #(#attrs)* }
|
||||||
});
|
};
|
||||||
let self_variants = variants.iter().map(|v| v.ctor(quote! { Self }));
|
let self_variant = variant.ctor(quote! { Self });
|
||||||
let content = events.iter().map(|(name, path)| to_event_path(name, path, kind, var));
|
let content = to_event_path(event.stable_name()?, &event.ev_path, kind, var);
|
||||||
let event_types: Vec<_> = events.iter().map(|(name, _)| name).collect();
|
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! {
|
Ok(quote! {
|
||||||
#[allow(unused_qualifications)]
|
#[allow(unused_qualifications)]
|
||||||
@ -187,13 +206,7 @@ fn expand_deserialize_impl(
|
|||||||
#ruma_common::serde::from_raw_json_value(&json)?;
|
#ruma_common::serde::from_raw_json_value(&json)?;
|
||||||
|
|
||||||
match &*ev_type {
|
match &*ev_type {
|
||||||
#(
|
#match_arms
|
||||||
#variant_attrs #event_types => {
|
|
||||||
let event = #serde_json::from_str::<#content>(json.get())
|
|
||||||
.map_err(D::Error::custom)?;
|
|
||||||
Ok(#self_variants(event))
|
|
||||||
},
|
|
||||||
)*
|
|
||||||
_ => {
|
_ => {
|
||||||
let event = #serde_json::from_str(json.get()).map_err(D::Error::custom)?;
|
let event = #serde_json::from_str(json.get()).map_err(D::Error::custom)?;
|
||||||
Ok(Self::_Custom(event))
|
Ok(Self::_Custom(event))
|
||||||
@ -293,11 +306,12 @@ fn expand_into_full_event(
|
|||||||
/// Create a content enum from `EventEnumInput`.
|
/// Create a content enum from `EventEnumInput`.
|
||||||
fn expand_content_enum(
|
fn expand_content_enum(
|
||||||
kind: EventKind,
|
kind: EventKind,
|
||||||
events: &[(LitStr, Path)],
|
events: &[EventEnumEntry],
|
||||||
|
docs: &[TokenStream],
|
||||||
attrs: &[Attribute],
|
attrs: &[Attribute],
|
||||||
variants: &[EventEnumVariant],
|
variants: &[EventEnumVariant],
|
||||||
ruma_common: &TokenStream,
|
ruma_common: &TokenStream,
|
||||||
) -> TokenStream {
|
) -> syn::Result<TokenStream> {
|
||||||
let serde = quote! { #ruma_common::exports::serde };
|
let serde = quote! { #ruma_common::exports::serde };
|
||||||
let serde_json = quote! { #ruma_common::exports::serde_json };
|
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 event_type_enum = kind.to_event_type_enum();
|
||||||
|
|
||||||
let content: Vec<_> =
|
let content: Vec<_> = events
|
||||||
events.iter().map(|(name, path)| to_event_content_path(kind, name, path, None)).collect();
|
.iter()
|
||||||
let event_type_match_arms = events.iter().map(|(s, _)| {
|
.map(|event| {
|
||||||
if let Some(prefix) = s.value().strip_suffix(".*") {
|
let stable_name = event.stable_name()?;
|
||||||
quote! { _s if _s.starts_with(#prefix) }
|
Ok(to_event_content_path(kind, stable_name, &event.ev_path, None))
|
||||||
} else {
|
})
|
||||||
quote! { #s }
|
.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()
|
||||||
});
|
});
|
||||||
let event_types: Vec<_> = events.iter().map(|(name, _)| name).collect();
|
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_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_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 from_impl = expand_from_impl(&ident, &content, variants);
|
||||||
|
|
||||||
let serialize_custom_event_error_path =
|
let serialize_custom_event_error_path =
|
||||||
quote! { #ruma_common::events::serialize_custom_event_error }.to_string();
|
quote! { #ruma_common::events::serialize_custom_event_error }.to_string();
|
||||||
|
|
||||||
quote! {
|
Ok(quote! {
|
||||||
#( #attrs )*
|
#( #attrs )*
|
||||||
#[derive(Clone, Debug, #serde::Serialize)]
|
#[derive(Clone, Debug, #serde::Serialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
@ -337,7 +373,7 @@ fn expand_content_enum(
|
|||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
pub enum #ident {
|
pub enum #ident {
|
||||||
#(
|
#(
|
||||||
#[doc = #event_types]
|
#docs
|
||||||
#variant_decls(#content),
|
#variant_decls(#content),
|
||||||
)*
|
)*
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
@ -363,12 +399,7 @@ fn expand_content_enum(
|
|||||||
input: &#serde_json::value::RawValue,
|
input: &#serde_json::value::RawValue,
|
||||||
) -> #serde_json::Result<Self> {
|
) -> #serde_json::Result<Self> {
|
||||||
match event_type {
|
match event_type {
|
||||||
#(
|
#event_type_match_arms
|
||||||
#variant_attrs #event_type_match_arms => {
|
|
||||||
let content = #content::from_parts(event_type, input)?;
|
|
||||||
::std::result::Result::Ok(#variant_ctors(content))
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
ty => {
|
ty => {
|
||||||
::std::result::Result::Ok(Self::_Custom {
|
::std::result::Result::Ok(Self::_Custom {
|
||||||
event_type: crate::PrivOwnedStr(ty.into()),
|
event_type: crate::PrivOwnedStr(ty.into()),
|
||||||
@ -379,7 +410,7 @@ fn expand_content_enum(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#from_impl
|
#from_impl
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expand_redact(
|
fn expand_redact(
|
||||||
@ -654,10 +685,75 @@ impl EventEnumEntry {
|
|||||||
|
|
||||||
pub(crate) fn to_variant(&self) -> syn::Result<EventEnumVariant> {
|
pub(crate) fn to_variant(&self) -> syn::Result<EventEnumVariant> {
|
||||||
let attrs = self.attrs.clone();
|
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 })
|
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 {
|
pub(crate) fn expand_from_impls_derived(input: DeriveInput) -> TokenStream {
|
||||||
|
@ -7,6 +7,7 @@ use quote::{format_ident, IdentFragment};
|
|||||||
use syn::{
|
use syn::{
|
||||||
braced,
|
braced,
|
||||||
parse::{self, Parse, ParseStream},
|
parse::{self, Parse, ParseStream},
|
||||||
|
punctuated::Punctuated,
|
||||||
Attribute, Ident, LitStr, Path, Token,
|
Attribute, Ident, LitStr, Path, Token,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -14,6 +15,7 @@ use syn::{
|
|||||||
mod kw {
|
mod kw {
|
||||||
syn::custom_keyword!(kind);
|
syn::custom_keyword!(kind);
|
||||||
syn::custom_keyword!(events);
|
syn::custom_keyword!(events);
|
||||||
|
syn::custom_keyword!(alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the variants of this enum change `to_event_path` needs to be updated as well.
|
// 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 struct EventEnumEntry {
|
||||||
pub attrs: Vec<Attribute>,
|
pub attrs: Vec<Attribute>,
|
||||||
|
pub aliases: Vec<LitStr>,
|
||||||
pub ev_type: LitStr,
|
pub ev_type: LitStr,
|
||||||
pub ev_path: Path,
|
pub ev_path: Path,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for EventEnumEntry {
|
impl Parse for EventEnumEntry {
|
||||||
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
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 ev_type: LitStr = input.parse()?;
|
||||||
let _: Token![=>] = input.parse()?;
|
let _: Token![=>] = input.parse()?;
|
||||||
let ev_path = input.call(Path::parse_mod_style)?;
|
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 })
|
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 {
|
let presence = vec![EventEnumEntry {
|
||||||
attrs: vec![],
|
attrs: vec![],
|
||||||
|
aliases: vec![],
|
||||||
ev_type: LitStr::new("m.presence", Span::call_site()),
|
ev_type: LitStr::new("m.presence", Span::call_site()),
|
||||||
ev_path: parse_quote! { #ruma_common::events::presence },
|
ev_path: parse_quote! { #ruma_common::events::presence },
|
||||||
}];
|
}];
|
||||||
@ -139,28 +140,32 @@ fn generate_enum(
|
|||||||
})
|
})
|
||||||
.collect::<syn::Result<_>>()?;
|
.collect::<syn::Result<_>>()?;
|
||||||
|
|
||||||
let from_str_match_arms: Vec<_> = deduped
|
let mut from_str_match_arms = TokenStream::new();
|
||||||
.iter()
|
for event in &deduped {
|
||||||
.map(|e| {
|
let v = event.to_variant()?;
|
||||||
let v = e.to_variant()?;
|
|
||||||
let ctor = v.ctor(quote! { Self });
|
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('*') {
|
if event.ev_type.value().ends_with(".*") {
|
||||||
quote! {
|
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
|
// Use if-let guard once available
|
||||||
_s if _s.starts_with(#prefix) => {
|
_s if _s.starts_with(#prefix) => {
|
||||||
#ctor(::std::convert::From::from(_s.strip_prefix(#prefix).unwrap()))
|
#ctor(::std::convert::From::from(_s.strip_prefix(#prefix).unwrap()))
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let t = &e.ev_type;
|
from_str_match_arms.extend(quote! { #(#attrs)* #(#ev_types)|* => #ctor, })
|
||||||
quote! { #t => #ctor }
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
let attrs = &e.attrs;
|
|
||||||
Ok(quote! { #(#attrs)* #match_arm })
|
|
||||||
})
|
|
||||||
.collect::<syn::Result<_>>()?;
|
|
||||||
|
|
||||||
let from_ident_for_room = if ident == "StateEventType" || ident == "MessageLikeEventType" {
|
let from_ident_for_room = if ident == "StateEventType" || ident == "MessageLikeEventType" {
|
||||||
let match_arms: Vec<_> = deduped
|
let match_arms: Vec<_> = deduped
|
||||||
@ -232,7 +237,7 @@ fn generate_enum(
|
|||||||
impl ::std::convert::From<&::std::primitive::str> for #ident {
|
impl ::std::convert::From<&::std::primitive::str> for #ident {
|
||||||
fn from(s: &::std::primitive::str) -> Self {
|
fn from(s: &::std::primitive::str) -> Self {
|
||||||
match s {
|
match s {
|
||||||
#(#from_str_match_arms,)*
|
#from_str_match_arms
|
||||||
_ => Self::_Custom(crate::PrivOwnedStr(::std::convert::From::from(s))),
|
_ => Self::_Custom(crate::PrivOwnedStr(::std::convert::From::from(s))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user