Implement MSC3925
… without a feature flag because it is a breaking change, so the feature would have to be non-additive, which is a no-go. Limited support for the previous replacement format is kept.
This commit is contained in:
parent
19d44489c3
commit
e61e0ccf6b
@ -74,7 +74,7 @@ impl StaticStateEventContent for CustomStateEventContent {
|
|||||||
// Like `StateUnsigned`, but without `prev_content`.
|
// Like `StateUnsigned`, but without `prev_content`.
|
||||||
// We don't care about `prev_content` since we'd only store the event type that is the same
|
// We don't care about `prev_content` since we'd only store the event type that is the same
|
||||||
// as in the content.
|
// as in the content.
|
||||||
type Unsigned = MessageLikeUnsigned;
|
type Unsigned = MessageLikeUnsigned<CustomMessageLikeEventContent>;
|
||||||
type PossiblyRedacted = Self;
|
type PossiblyRedacted = Self;
|
||||||
}
|
}
|
||||||
impl PossiblyRedactedStateEventContent for CustomStateEventContent {
|
impl PossiblyRedactedStateEventContent for CustomStateEventContent {
|
||||||
|
@ -119,7 +119,7 @@ pub struct OriginalMessageLikeEvent<C: MessageLikeEventContent> {
|
|||||||
pub room_id: OwnedRoomId,
|
pub room_id: OwnedRoomId,
|
||||||
|
|
||||||
/// Additional key-value pairs not signed by the homeserver.
|
/// Additional key-value pairs not signed by the homeserver.
|
||||||
pub unsigned: MessageLikeUnsigned,
|
pub unsigned: MessageLikeUnsigned<C>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An unredacted message-like event without a `room_id`.
|
/// An unredacted message-like event without a `room_id`.
|
||||||
@ -141,7 +141,7 @@ pub struct OriginalSyncMessageLikeEvent<C: MessageLikeEventContent> {
|
|||||||
pub origin_server_ts: MilliSecondsSinceUnixEpoch,
|
pub origin_server_ts: MilliSecondsSinceUnixEpoch,
|
||||||
|
|
||||||
/// Additional key-value pairs not signed by the homeserver.
|
/// Additional key-value pairs not signed by the homeserver.
|
||||||
pub unsigned: MessageLikeUnsigned,
|
pub unsigned: MessageLikeUnsigned<C>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A redacted message-like event.
|
/// A redacted message-like event.
|
||||||
|
@ -10,9 +10,11 @@ use serde::{Deserialize, Serialize};
|
|||||||
use super::AnyMessageLikeEvent;
|
use super::AnyMessageLikeEvent;
|
||||||
use crate::{
|
use crate::{
|
||||||
serde::{Raw, StringEnum},
|
serde::{Raw, StringEnum},
|
||||||
MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedUserId, PrivOwnedStr,
|
OwnedEventId, PrivOwnedStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod rel_serde;
|
||||||
|
|
||||||
/// Information about the event a [rich reply] is replying to.
|
/// Information about the event a [rich reply] is replying to.
|
||||||
///
|
///
|
||||||
/// [rich reply]: https://spec.matrix.org/latest/client-server-api/#rich-replies
|
/// [rich reply]: https://spec.matrix.org/latest/client-server-api/#rich-replies
|
||||||
@ -58,31 +60,6 @@ impl Annotation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A bundled replacement.
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
|
||||||
pub struct BundledReplacement {
|
|
||||||
/// The ID of the replacing event.
|
|
||||||
pub event_id: OwnedEventId,
|
|
||||||
|
|
||||||
/// The user ID of the sender of the latest replacement.
|
|
||||||
pub sender: OwnedUserId,
|
|
||||||
|
|
||||||
/// Timestamp in milliseconds on originating homeserver when the latest replacement was sent.
|
|
||||||
pub origin_server_ts: MilliSecondsSinceUnixEpoch,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BundledReplacement {
|
|
||||||
/// Creates a new `BundledReplacement` with the given event ID, sender and timestamp.
|
|
||||||
pub fn new(
|
|
||||||
event_id: OwnedEventId,
|
|
||||||
sender: OwnedUserId,
|
|
||||||
origin_server_ts: MilliSecondsSinceUnixEpoch,
|
|
||||||
) -> Self {
|
|
||||||
Self { event_id, sender, origin_server_ts }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The content of a [replacement] relation.
|
/// The content of a [replacement] relation.
|
||||||
///
|
///
|
||||||
/// [replacement]: https://spec.matrix.org/latest/client-server-api/#event-replacements
|
/// [replacement]: https://spec.matrix.org/latest/client-server-api/#event-replacements
|
||||||
@ -221,12 +198,18 @@ impl ReferenceChunk {
|
|||||||
/// [Bundled aggregations] of related child events of a message-like event.
|
/// [Bundled aggregations] of related child events of a message-like event.
|
||||||
///
|
///
|
||||||
/// [Bundled aggregations]: https://spec.matrix.org/latest/client-server-api/#aggregations
|
/// [Bundled aggregations]: https://spec.matrix.org/latest/client-server-api/#aggregations
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
pub struct BundledMessageLikeRelations {
|
pub struct BundledMessageLikeRelations<E> {
|
||||||
/// Replacement relation.
|
/// Replacement relation.
|
||||||
#[serde(rename = "m.replace", skip_serializing_if = "Option::is_none")]
|
#[serde(rename = "m.replace", skip_serializing_if = "Option::is_none")]
|
||||||
pub replace: Option<Box<BundledReplacement>>,
|
pub replace: Option<Box<E>>,
|
||||||
|
|
||||||
|
/// Set when the above fails to deserialize.
|
||||||
|
///
|
||||||
|
/// Intentionally *not* public.
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
has_invalid_replacement: bool,
|
||||||
|
|
||||||
/// Thread relation.
|
/// Thread relation.
|
||||||
#[serde(rename = "m.thread", skip_serializing_if = "Option::is_none")]
|
#[serde(rename = "m.thread", skip_serializing_if = "Option::is_none")]
|
||||||
@ -237,10 +220,20 @@ pub struct BundledMessageLikeRelations {
|
|||||||
pub reference: Option<Box<ReferenceChunk>>,
|
pub reference: Option<Box<ReferenceChunk>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BundledMessageLikeRelations {
|
impl<E> BundledMessageLikeRelations<E> {
|
||||||
/// Creates a new empty `BundledMessageLikeRelations`.
|
/// Creates a new empty `BundledMessageLikeRelations`.
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self { replace: None, thread: None, reference: None }
|
Self { replace: None, has_invalid_replacement: false, thread: None, reference: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this bundle contains a replacement relation.
|
||||||
|
///
|
||||||
|
/// This may be `true` even if the `replace` field is `None`, because Matrix versions prior to
|
||||||
|
/// 1.7 had a different incompatible format for bundled replacements. Use this method to check
|
||||||
|
/// whether an event was replaced. If this returns `true` but `replace` is `None`, use one of
|
||||||
|
/// the endpoints from `ruma::api::client::relations` to fetch the relation details.
|
||||||
|
pub fn has_replacement(&self) -> bool {
|
||||||
|
self.replace.is_some() || self.has_invalid_replacement
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if all fields are empty.
|
/// Returns `true` if all fields are empty.
|
||||||
@ -249,6 +242,12 @@ impl BundledMessageLikeRelations {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E> Default for BundledMessageLikeRelations<E> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// [Bundled aggregations] of related child events of a state event.
|
/// [Bundled aggregations] of related child events of a state event.
|
||||||
///
|
///
|
||||||
/// [Bundled aggregations]: https://spec.matrix.org/latest/client-server-api/#aggregations
|
/// [Bundled aggregations]: https://spec.matrix.org/latest/client-server-api/#aggregations
|
||||||
|
35
crates/ruma-common/src/events/relation/rel_serde.rs
Normal file
35
crates/ruma-common/src/events/relation/rel_serde.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use serde::{de::DeserializeOwned, Deserialize, Deserializer};
|
||||||
|
|
||||||
|
use super::{BundledMessageLikeRelations, BundledThread, ReferenceChunk};
|
||||||
|
use crate::serde::Raw;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct BundledMessageLikeRelationsJsonRepr<E> {
|
||||||
|
#[serde(rename = "m.replace")]
|
||||||
|
replace: Option<Raw<Box<E>>>,
|
||||||
|
#[serde(rename = "m.thread")]
|
||||||
|
thread: Option<Box<BundledThread>>,
|
||||||
|
#[serde(rename = "m.reference")]
|
||||||
|
reference: Option<Box<ReferenceChunk>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de, E> Deserialize<'de> for BundledMessageLikeRelations<E>
|
||||||
|
where
|
||||||
|
E: DeserializeOwned,
|
||||||
|
{
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let BundledMessageLikeRelationsJsonRepr { replace, thread, reference } =
|
||||||
|
BundledMessageLikeRelationsJsonRepr::deserialize(deserializer)?;
|
||||||
|
|
||||||
|
let (replace, has_invalid_replacement) =
|
||||||
|
match replace.as_ref().map(Raw::deserialize).transpose() {
|
||||||
|
Ok(replace) => (replace, false),
|
||||||
|
Err(_) => (None, true),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(BundledMessageLikeRelations { replace, has_invalid_replacement, thread, reference })
|
||||||
|
}
|
||||||
|
}
|
@ -60,7 +60,7 @@ pub struct OriginalRoomRedactionEvent {
|
|||||||
pub room_id: OwnedRoomId,
|
pub room_id: OwnedRoomId,
|
||||||
|
|
||||||
/// Additional key-value pairs not signed by the homeserver.
|
/// Additional key-value pairs not signed by the homeserver.
|
||||||
pub unsigned: MessageLikeUnsigned,
|
pub unsigned: MessageLikeUnsigned<RoomRedactionEventContent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Redacted redaction event.
|
/// Redacted redaction event.
|
||||||
@ -106,7 +106,7 @@ pub struct OriginalSyncRoomRedactionEvent {
|
|||||||
pub origin_server_ts: MilliSecondsSinceUnixEpoch,
|
pub origin_server_ts: MilliSecondsSinceUnixEpoch,
|
||||||
|
|
||||||
/// Additional key-value pairs not signed by the homeserver.
|
/// Additional key-value pairs not signed by the homeserver.
|
||||||
pub unsigned: MessageLikeUnsigned,
|
pub unsigned: MessageLikeUnsigned<RoomRedactionEventContent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Redacted redaction event without a `room_id`.
|
/// Redacted redaction event without a `room_id`.
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
use js_int::Int;
|
use js_int::Int;
|
||||||
use serde::Deserialize;
|
use serde::{de::DeserializeOwned, Deserialize};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
relation::{BundledMessageLikeRelations, BundledStateRelations},
|
relation::{BundledMessageLikeRelations, BundledStateRelations},
|
||||||
room::redaction::RoomRedactionEventContent,
|
room::redaction::RoomRedactionEventContent,
|
||||||
PossiblyRedactedStateEventContent,
|
MessageLikeEventContent, OriginalSyncMessageLikeEvent, PossiblyRedactedStateEventContent,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
serde::CanBeEmpty, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedTransactionId, OwnedUserId,
|
serde::CanBeEmpty, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedTransactionId, OwnedUserId,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Extra information about a message event that is not incorporated into the event's hash.
|
/// Extra information about a message event that is not incorporated into the event's hash.
|
||||||
#[derive(Clone, Debug, Default, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
#[serde(bound = "OriginalSyncMessageLikeEvent<C>: DeserializeOwned")]
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
pub struct MessageLikeUnsigned {
|
pub struct MessageLikeUnsigned<C: MessageLikeEventContent> {
|
||||||
/// The time in milliseconds that has elapsed since the event was sent.
|
/// The time in milliseconds that has elapsed since the event was sent.
|
||||||
///
|
///
|
||||||
/// This field is generated by the local homeserver, and may be incorrect if the local time on
|
/// This field is generated by the local homeserver, and may be incorrect if the local time on
|
||||||
@ -29,17 +30,23 @@ pub struct MessageLikeUnsigned {
|
|||||||
///
|
///
|
||||||
/// [Bundled aggregations]: https://spec.matrix.org/latest/client-server-api/#aggregations
|
/// [Bundled aggregations]: https://spec.matrix.org/latest/client-server-api/#aggregations
|
||||||
#[serde(rename = "m.relations", default)]
|
#[serde(rename = "m.relations", default)]
|
||||||
pub relations: BundledMessageLikeRelations,
|
pub relations: BundledMessageLikeRelations<OriginalSyncMessageLikeEvent<C>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageLikeUnsigned {
|
impl<C: MessageLikeEventContent> MessageLikeUnsigned<C> {
|
||||||
/// Create a new `Unsigned` with fields set to `None`.
|
/// Create a new `Unsigned` with fields set to `None`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self { age: None, transaction_id: None, relations: BundledMessageLikeRelations::default() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CanBeEmpty for MessageLikeUnsigned {
|
impl<C: MessageLikeEventContent> Default for MessageLikeUnsigned<C> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: MessageLikeEventContent> CanBeEmpty for MessageLikeUnsigned<C> {
|
||||||
/// Whether this unsigned data is empty (all fields are `None`).
|
/// Whether this unsigned data is empty (all fields are `None`).
|
||||||
///
|
///
|
||||||
/// This method is used to determine whether to skip serializing the `unsigned` field in room
|
/// This method is used to determine whether to skip serializing the `unsigned` field in room
|
||||||
@ -142,5 +149,5 @@ pub struct UnsignedRoomRedactionEvent {
|
|||||||
|
|
||||||
/// Additional key-value pairs not signed by the homeserver.
|
/// Additional key-value pairs not signed by the homeserver.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub unsigned: MessageLikeUnsigned,
|
pub unsigned: MessageLikeUnsigned<RoomRedactionEventContent>,
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user