use js_int::UInt;
use ruma_common::{OwnedEventId, OwnedRoomId};
use ruma_macros::EventContent;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
#[ruma_event(
type = "org.matrix.msc4471.stream.update",
alias = "m.stream.update",
kind = ToDevice,
)]
pub struct ToDeviceStreamUpdateEventContent {
pub room_id: OwnedRoomId,
pub event_id: OwnedEventId,
pub seq: UInt,
#[serde(flatten)]
pub operation: StreamUpdateOperation,
}
impl ToDeviceStreamUpdateEventContent {
pub fn new(
room_id: OwnedRoomId,
event_id: OwnedEventId,
seq: UInt,
operation: StreamUpdateOperation,
) -> Self {
Self { room_id, event_id, seq, operation }
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
#[serde(tag = "op", content = "content", rename_all = "snake_case")]
pub enum StreamUpdateOperation {
Replace(StreamUpdateContent),
Append(StreamUpdateContent),
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
pub struct StreamUpdateContent {
pub body: String,
}
impl StreamUpdateContent {
pub fn new(body: String) -> Self {
Self { body }
}
}
#[cfg(test)]
mod tests {
use assert_matches2::assert_matches;
use js_int::uint;
use ruma_common::{
canonical_json::assert_to_canonical_json_eq, owned_event_id, owned_room_id, serde::Raw,
};
use serde_json::{from_value as from_json_value, json};
use super::{StreamUpdateContent, StreamUpdateOperation, ToDeviceStreamUpdateEventContent};
use crate::{AnyToDeviceEvent, ToDeviceEvent};
#[test]
fn replace_update_round_trip() {
let content = ToDeviceStreamUpdateEventContent::new(
owned_room_id!("!room:example.org"),
owned_event_id!("$event:example.org"),
uint!(1),
StreamUpdateOperation::Replace(StreamUpdateContent::new("hello".to_owned())),
);
assert_to_canonical_json_eq!(
content,
json!({
"room_id": "!room:example.org",
"event_id": "$event:example.org",
"seq": 1,
"op": "replace",
"content": {
"body": "hello",
},
})
);
let deserialized: ToDeviceStreamUpdateEventContent =
Raw::new(&content).unwrap().deserialize().unwrap();
assert_eq!(deserialized.seq, uint!(1));
assert_matches!(deserialized.operation, StreamUpdateOperation::Replace(payload));
assert_eq!(payload.body, "hello");
}
#[test]
fn replace_update_seq_zero_round_trip() {
let content = ToDeviceStreamUpdateEventContent::new(
owned_room_id!("!room:example.org"),
owned_event_id!("$event:example.org"),
uint!(0),
StreamUpdateOperation::Replace(StreamUpdateContent::new("hello".to_owned())),
);
assert_to_canonical_json_eq!(
content,
json!({
"room_id": "!room:example.org",
"event_id": "$event:example.org",
"seq": 0,
"op": "replace",
"content": {
"body": "hello",
},
})
);
let deserialized: ToDeviceStreamUpdateEventContent =
Raw::new(&content).unwrap().deserialize().unwrap();
assert_eq!(deserialized.seq, uint!(0));
assert_matches!(deserialized.operation, StreamUpdateOperation::Replace(payload));
assert_eq!(payload.body, "hello");
}
#[test]
fn append_update_round_trip() {
let content = ToDeviceStreamUpdateEventContent::new(
owned_room_id!("!room:example.org"),
owned_event_id!("$event:example.org"),
uint!(2),
StreamUpdateOperation::Append(StreamUpdateContent::new(" world".to_owned())),
);
assert_to_canonical_json_eq!(
content,
json!({
"room_id": "!room:example.org",
"event_id": "$event:example.org",
"seq": 2,
"op": "append",
"content": {
"body": " world",
},
})
);
}
#[test]
fn any_to_device_update() {
let event = json!({
"sender": "@alice:example.org",
"type": "org.matrix.msc4471.stream.update",
"content": {
"room_id": "!room:example.org",
"event_id": "$event:example.org",
"seq": 1,
"op": "replace",
"content": {
"body": "hello",
},
},
});
let event = from_json_value::<AnyToDeviceEvent>(event).unwrap();
assert_matches!(event, AnyToDeviceEvent::StreamUpdate(ToDeviceEvent { content, .. }));
assert_matches!(content.operation, StreamUpdateOperation::Replace(payload));
assert_eq!(payload.body, "hello");
}
#[test]
fn any_to_device_update_stable_alias() {
let event = json!({
"sender": "@alice:example.org",
"type": "m.stream.update",
"content": {
"room_id": "!room:example.org",
"event_id": "$event:example.org",
"seq": 1,
"op": "replace",
"content": {
"body": "hello",
},
},
});
let event = from_json_value::<AnyToDeviceEvent>(event).unwrap();
assert_matches!(event, AnyToDeviceEvent::StreamUpdate(ToDeviceEvent { content, .. }));
assert_matches!(content.operation, StreamUpdateOperation::Replace(payload));
assert_eq!(payload.body, "hello");
}
}