diff --git a/crates/ruma-events-macros/Cargo.toml b/crates/ruma-events-macros/Cargo.toml index 6f29b62b..2236590e 100644 --- a/crates/ruma-events-macros/Cargo.toml +++ b/crates/ruma-events-macros/Cargo.toml @@ -18,6 +18,9 @@ version = "0.24.3" [lib] proc-macro = true +[features] +compat = [] + [dependencies] proc-macro-crate = "1.0.0" proc-macro2 = "1.0.24" diff --git a/crates/ruma-events-macros/src/event.rs b/crates/ruma-events-macros/src/event.rs index ad6c925d..80b9fab2 100644 --- a/crates/ruma-events-macros/src/event.rs +++ b/crates/ruma-events-macros/src/event.rs @@ -44,7 +44,7 @@ pub fn expand_event(input: DeriveInput) -> syn::Result { }; let serialize_impl = expand_serialize_event(&input, &var, &fields, &ruma_events); - let deserialize_impl = expand_deserialize_event(&input, &var, &fields, &ruma_events)?; + let deserialize_impl = expand_deserialize_event(&input, &kind, &var, &fields, &ruma_events)?; let conversion_impl = expand_from_into(&input, &kind, &var, &fields, &ruma_events); let eq_impl = expand_eq_ord_event(&input, &fields); @@ -120,6 +120,7 @@ fn expand_serialize_event( fn expand_deserialize_event( input: &DeriveInput, + _kind: &EventKind, var: &EventKindVariation, fields: &[Field], ruma_events: &TokenStream, @@ -154,12 +155,30 @@ fn expand_deserialize_event( let ty = &field.ty; if name == "content" || name == "prev_content" { if is_generic { - quote! { Box<#serde_json::value::RawValue> } + quote! { ::std::boxed::Box<#serde_json::value::RawValue> } } else { quote! { #content_type } } } else { - quote! { #ty } + #[allow(unused_mut)] + let mut ty = quote! { #ty }; + + #[cfg(feature = "compat")] + if matches!(_kind, EventKind::State) && name == "unsigned" { + match var { + EventKindVariation::Full | EventKindVariation::Sync => { + ty = quote! { #ruma_events::UnsignedWithPrevContent }; + } + EventKindVariation::Redacted | EventKindVariation::RedactedSync => { + ty = quote! { #ruma_events::RedactedUnsignedWithPrevContent }; + } + EventKindVariation::Stripped + | EventKindVariation::Initial + | EventKindVariation::RedactedStripped => unreachable!(), + } + } + + ty } }) .collect(); @@ -207,16 +226,43 @@ fn expand_deserialize_event( } } else if name == "prev_content" { if is_generic { - quote! { + #[allow(unused_mut)] + let mut res = quote! { let prev_content = prev_content.map(|json| { C::from_parts(&event_type, &json).map_err(A::Error::custom) }).transpose()?; - } + }; + + #[cfg(feature = "compat")] + if let EventKind::State = _kind { + res = quote! { + let prev_content = prev_content + .or_else(|| unsigned.as_mut().and_then(|u| u.prev_content.take())); + #res + }; + }; + + res } else { TokenStream::new() } } else if name == "unsigned" { - quote! { let unsigned = unsigned.unwrap_or_default(); } + #[allow(unused_mut)] + let mut res = quote! { + let unsigned = unsigned.unwrap_or_default(); + }; + + #[cfg(feature = "compat")] + if matches!(_kind, EventKind::State) { + res = quote! { + let unsigned = unsigned.map_or_else( + ::std::default::Default::default, + ::std::convert::From::from, + ); + }; + } + + res } else { let attrs: Vec<_> = field .attrs diff --git a/crates/ruma-events/Cargo.toml b/crates/ruma-events/Cargo.toml index 8ee1fbd9..028a702c 100644 --- a/crates/ruma-events/Cargo.toml +++ b/crates/ruma-events/Cargo.toml @@ -16,7 +16,7 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [features] -compat = [] +compat = ["ruma-events-macros/compat"] markdown = ["pulldown-cmark"] unstable-exhaustive-types = [] diff --git a/crates/ruma-events/src/lib.rs b/crates/ruma-events/src/lib.rs index 6e9ba9e4..f4810675 100644 --- a/crates/ruma-events/src/lib.rs +++ b/crates/ruma-events/src/lib.rs @@ -194,6 +194,10 @@ pub use self::{ unsigned::{RedactedUnsigned, Unsigned}, }; +#[doc(hidden)] +#[cfg(feature = "compat")] +pub use unsigned::{RedactedUnsignedWithPrevContent, UnsignedWithPrevContent}; + /// The base trait that all event content types implement. /// /// Implementing this trait allows content types to be serialized as well as deserialized. diff --git a/crates/ruma-events/src/unsigned.rs b/crates/ruma-events/src/unsigned.rs index b8d38b52..416f42d3 100644 --- a/crates/ruma-events/src/unsigned.rs +++ b/crates/ruma-events/src/unsigned.rs @@ -74,3 +74,50 @@ impl RedactedUnsigned { self.redacted_because.is_none() } } + +#[doc(hidden)] +#[cfg(feature = "compat")] +#[derive(Deserialize)] +pub struct UnsignedWithPrevContent { + #[serde(skip_serializing_if = "Option::is_none")] + age: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + transaction_id: Option, + + #[cfg(feature = "unstable-pre-spec")] + #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))] + #[serde(rename = "m.relations", skip_serializing_if = "Option::is_none")] + relations: Option, + + pub prev_content: Option>, +} + +#[cfg(feature = "compat")] +impl From for Unsigned { + fn from(u: UnsignedWithPrevContent) -> Self { + Self { + age: u.age, + transaction_id: u.transaction_id, + #[cfg(feature = "unstable-pre-spec")] + relations: u.relations, + } + } +} + +#[doc(hidden)] +#[cfg(feature = "compat")] +#[derive(Deserialize)] +pub struct RedactedUnsignedWithPrevContent { + #[serde(skip_serializing_if = "Option::is_none")] + redacted_because: Option>, + + pub prev_content: Option>, +} + +#[cfg(feature = "compat")] +impl From for RedactedUnsigned { + fn from(u: RedactedUnsignedWithPrevContent) -> Self { + Self { redacted_because: u.redacted_because } + } +}