diff --git a/crates/ruma-client-api/src/state/get_state_events_for_key.rs b/crates/ruma-client-api/src/state/get_state_events_for_key.rs index ca9a8aed..079a1053 100644 --- a/crates/ruma-client-api/src/state/get_state_events_for_key.rs +++ b/crates/ruma-client-api/src/state/get_state_events_for_key.rs @@ -86,8 +86,8 @@ pub mod v3 { use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; let room_id_percent = utf8_percent_encode(self.room_id.as_str(), NON_ALPHANUMERIC); - let event_type_percent = - utf8_percent_encode(self.event_type.as_str(), NON_ALPHANUMERIC); + let event_type = self.event_type.to_string(); + let event_type_percent = utf8_percent_encode(&event_type, NON_ALPHANUMERIC); let mut url = format!( "{}{}", diff --git a/crates/ruma-client-api/src/state/send_state_event.rs b/crates/ruma-client-api/src/state/send_state_event.rs index f2f6d4ac..6b8ec66b 100644 --- a/crates/ruma-client-api/src/state/send_state_event.rs +++ b/crates/ruma-client-api/src/state/send_state_event.rs @@ -114,8 +114,8 @@ pub mod v3 { use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; let room_id_percent = utf8_percent_encode(self.room_id.as_str(), NON_ALPHANUMERIC); - let event_type_percent = - utf8_percent_encode(self.event_type.as_str(), NON_ALPHANUMERIC); + let event_type = self.event_type.to_string(); + let event_type_percent = utf8_percent_encode(&event_type, NON_ALPHANUMERIC); let mut url = format!( "{}{}", diff --git a/crates/ruma-common/tests/events/enums.rs b/crates/ruma-common/tests/events/enums.rs index 1c2c28ea..5b337c11 100644 --- a/crates/ruma-common/tests/events/enums.rs +++ b/crates/ruma-common/tests/events/enums.rs @@ -306,7 +306,7 @@ fn alias_event_field_access() { } else { panic!("the `Any*Event` enum's accessor methods may have been altered") } - assert_eq!(deser.event_type().as_str(), "m.room.aliases"); + assert_eq!(deser.event_type().to_string(), "m.room.aliases"); } #[test] diff --git a/crates/ruma-common/tests/events/ui/10-content-wildcard.rs b/crates/ruma-common/tests/events/ui/10-content-wildcard.rs index 1ecdd340..f3dbb916 100644 --- a/crates/ruma-common/tests/events/ui/10-content-wildcard.rs +++ b/crates/ruma-common/tests/events/ui/10-content-wildcard.rs @@ -12,7 +12,7 @@ fn main() { use ruma_common::events::EventContent; assert_eq!( - MacroTestContent { frag: "foo".to_owned() }.event_type().as_str(), + MacroTestContent { frag: "foo".to_owned() }.event_type().to_string(), "m.macro.test.foo" ); } diff --git a/crates/ruma-macros/src/events/event.rs b/crates/ruma-macros/src/events/event.rs index fbdba084..8d51029c 100644 --- a/crates/ruma-macros/src/events/event.rs +++ b/crates/ruma-macros/src/events/event.rs @@ -119,14 +119,13 @@ fn expand_serialize_event( { use #serde::ser::{SerializeStruct as _, Error as _}; - let event_type = #ruma_common::events::EventContent::event_type(&self.content); - let event_type = - ::std::convert::AsRef::<::std::primitive::str>::as_ref(&event_type); - let mut state = serializer.serialize_struct(stringify!(#ident), 7)?; - state.serialize_field("type", event_type)?; + let event_type = #ruma_common::events::EventContent::event_type(&self.content); + state.serialize_field("type", &event_type)?; + #( #serialize_fields )* + state.end() } } diff --git a/crates/ruma-macros/src/events/event_content.rs b/crates/ruma-macros/src/events/event_content.rs index 86d55f2e..0df9b62d 100644 --- a/crates/ruma-macros/src/events/event_content.rs +++ b/crates/ruma-macros/src/events/event_content.rs @@ -474,9 +474,13 @@ fn generate_event_content_impl<'a>( ty: ::std::option::Option, } - impl ::std::convert::AsRef<::std::primitive::str> for #i { - fn as_ref(&self) -> &::std::primitive::str { - self.ty.as_ref().map(|t| &t.0[..]).unwrap_or(#event_type) + impl #serde::Serialize for #i { + fn serialize(&self, serializer: S) -> ::std::result::Result + where + S: #serde::Serializer, + { + let s = self.ty.as_ref().map(|t| &t.0[..]).unwrap_or(#event_type); + serializer.serialize_str(s) } } }); diff --git a/crates/ruma-macros/src/events/event_enum.rs b/crates/ruma-macros/src/events/event_enum.rs index abd85d11..9492d70b 100644 --- a/crates/ruma-macros/src/events/event_enum.rs +++ b/crates/ruma-macros/src/events/event_enum.rs @@ -240,7 +240,7 @@ fn expand_into_full_event( /// Create a content enum from `EventEnumInput`. fn expand_content_enum( kind: EventKind, - events: &[LitStr], + event_types: &[LitStr], attrs: &[Attribute], variants: &[EventEnumVariant], ruma_common: &TokenStream, @@ -251,13 +251,18 @@ fn expand_content_enum( let ident = kind.to_content_enum(); let event_type_enum = kind.to_event_type_enum(); - let event_type_str = events; let content: Vec<_> = - events.iter().map(|ev| to_event_content_path(kind, ev, None, ruma_common)).collect(); + event_types.iter().map(|ev| to_event_content_path(kind, ev, None, ruma_common)).collect(); + let event_type_match_arms = event_types.iter().map(|s| { + if let Some(prefix) = s.value().strip_suffix(".*") { + quote! { _s if _s.starts_with(#prefix) } + } else { + quote! { #s } + } + }); let variant_decls = variants.iter().map(|v| v.decl()).collect::>(); - let variant_attrs = variants.iter().map(|v| { let attrs = &v.attrs; quote! { #(#attrs)* } @@ -278,7 +283,7 @@ fn expand_content_enum( #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] pub enum #ident { #( - #[doc = #event_type_str] + #[doc = #event_types] #variant_decls(#content), )* #[doc(hidden)] @@ -305,7 +310,7 @@ fn expand_content_enum( ) -> #serde_json::Result { match event_type { #( - #variant_attrs #event_type_str => { + #variant_attrs #event_type_match_arms => { let content = #content::from_parts(event_type, input)?; ::std::result::Result::Ok(#variant_ctors(content)) } @@ -434,7 +439,9 @@ fn expand_accessor_methods( event.unsigned._map_prev_unsigned(|c| #content_enum::_Custom { event_type: crate::PrivOwnedStr( ::std::convert::From::from( - #ruma_common::events::EventContent::event_type(c).as_str() + ::std::string::ToString::to_string( + &#ruma_common::events::EventContent::event_type(c) + ) ), ), }) @@ -467,8 +474,9 @@ fn expand_accessor_methods( Self::_Custom(event) => #content_enum::_Custom { event_type: crate::PrivOwnedStr( ::std::convert::From::from( - #ruma_common::events::EventContent::event_type(&event.content) - .as_str(), + ::std::string::ToString::to_string( + &#ruma_common::events::EventContent::event_type(&event.content) + ) ), ), }, @@ -627,9 +635,14 @@ impl EventEnumVariant { } impl EventEnumEntry { + pub(crate) fn has_type_fragment(&self) -> bool { + self.ev_type.value().ends_with(".*") + } + pub(crate) fn to_variant(&self) -> syn::Result { let attrs = self.attrs.clone(); let ident = m_prefix_name_to_type_name(&self.ev_type)?; + Ok(EventEnumVariant { attrs, ident }) } } diff --git a/crates/ruma-macros/src/events/event_type.rs b/crates/ruma-macros/src/events/event_type.rs index 5d8789e0..88f7a5ee 100644 --- a/crates/ruma-macros/src/events/event_type.rs +++ b/crates/ruma-macros/src/events/event_type.rs @@ -61,8 +61,7 @@ fn generate_enum( input: &[&Vec], ruma_common: &TokenStream, ) -> syn::Result { - let str_doc = format!("Creates a string slice from this `{}`.", ident); - let byte_doc = format!("Creates a byte slice from this `{}`.", ident); + let serde = quote! { #ruma_common::exports::serde }; let enum_doc = format!("The type of `{}` this is.", ident.strip_suffix("Type").unwrap()); let deprecated_attr = (ident == "EventType").then(|| { @@ -86,8 +85,57 @@ fn generate_enum( } let event_types = deduped.iter().map(|e| &e.ev_type); - let variants = - deduped.iter().map(|e| Ok(e.to_variant()?.decl())).collect::>>()?; + + let variants: Vec<_> = deduped + .iter() + .map(|e| { + let start = e.to_variant()?.decl(); + let data = e.has_type_fragment().then(|| quote! { (::std::string::String) }); + + Ok(quote! { + #start #data + }) + }) + .collect::>()?; + + let to_cow_str_match_arms: Vec<_> = deduped + .iter() + .map(|e| { + let v = e.to_variant()?; + let start = v.match_arm(quote! { Self }); + let ev_type = &e.ev_type; + + Ok(if let Some(prefix) = ev_type.value().strip_suffix(".*") { + let fstr = prefix.to_owned() + "{}"; + quote! { #start(_s) => ::std::borrow::Cow::Owned(::std::format!(#fstr, _s)) } + } else { + quote! { #start => ::std::borrow::Cow::Borrowed(#ev_type) } + }) + }) + .collect::>()?; + + let from_str_match_arms: Vec<_> = deduped + .iter() + .map(|e| { + let v = e.to_variant()?; + let ctor = v.ctor(quote! { Self }); + + let match_arm = if let Some(prefix) = e.ev_type.value().strip_suffix('*') { + quote! { + // 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::>()?; Ok(quote! { #[doc = #enum_doc] @@ -95,12 +143,11 @@ fn generate_enum( /// This type can hold an arbitrary string. To check for events that are not available as a /// documented variant here, use its string representation, obtained through `.as_str()`. #deprecated_attr - #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, #ruma_common::serde::StringEnum)] + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] pub enum #ident { #( #[doc = #event_types] - #[ruma_enum(rename = #event_types)] #variants, )* #[doc(hidden)] @@ -109,14 +156,56 @@ fn generate_enum( #[allow(deprecated)] impl #ident { - #[doc = #str_doc] - pub fn as_str(&self) -> &str { - self.as_ref() + fn to_cow_str(&self) -> ::std::borrow::Cow<'_, ::std::primitive::str> { + match self { + #(#to_cow_str_match_arms,)* + Self::_Custom(crate::PrivOwnedStr(s)) => ::std::borrow::Cow::Borrowed(s), + } } + } - #[doc = #byte_doc] - pub fn as_bytes(&self) -> &[u8] { - self.as_str().as_bytes() + #[allow(deprecated)] + impl ::std::fmt::Display for #ident { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + self.to_cow_str().fmt(f) + } + } + + #[allow(deprecated)] + impl ::std::convert::From<&::std::primitive::str> for #ident { + fn from(s: &::std::primitive::str) -> Self { + match s { + #(#from_str_match_arms,)* + _ => Self::_Custom(crate::PrivOwnedStr(::std::convert::From::from(s))), + } + } + } + + #[allow(deprecated)] + impl ::std::convert::From<::std::string::String> for #ident { + fn from(s: ::std::string::String) -> Self { + ::std::convert::From::from(s.as_str()) + } + } + + #[allow(deprecated)] + impl<'de> #serde::Deserialize<'de> for #ident { + fn deserialize(deserializer: D) -> ::std::result::Result + where + D: #serde::Deserializer<'de> + { + let s = #ruma_common::serde::deserialize_cow_str(deserializer)?; + Ok(::std::convert::From::from(&s[..])) + } + } + + #[allow(deprecated)] + impl #serde::Serialize for #ident { + fn serialize(&self, serializer: S) -> ::std::result::Result + where + S: #serde::Serializer, + { + self.to_cow_str().serialize(serializer) } } })