use ruma::{events::AnySyncTimelineEvent, serde::Raw};
use serde::Deserialize;
use crate::deserialized_responses::EncryptionInfo;
#[derive(Debug, thiserror::Error)]
pub enum EditValidityError {
#[error(
"the sender of the original event isn't the same as the sender of the replacement event"
)]
InvalidSender,
#[error("the original event or the replacement event contains a state key")]
StateKeyPresent,
#[error(
"the content type of the original event is `{content_type}` while the replacement is a `{replacement_type}`"
)]
MismatchContentType {
content_type: String,
replacement_type: String,
},
#[error("the original event is an edit as well")]
OriginalEventIsReplacement,
#[error("the replacement event is not a replacement for the original event")]
NotReplacement,
#[error("the event was encrypted, as such it should have an `m.new_content` field")]
MissingNewContent,
#[error(transparent)]
InvalidJson(#[from] serde_json::Error),
#[error("the original event was encrypted while the replacement is not")]
ReplacementNotEncrypted,
}
pub fn check_validity_of_replacement_events(
original_json: &Raw<AnySyncTimelineEvent>,
original_encryption_info: Option<&EncryptionInfo>,
replacement_json: &Raw<AnySyncTimelineEvent>,
replacement_encryption_info: Option<&EncryptionInfo>,
) -> Result<(), EditValidityError> {
const REPLACEMENT_REL_TYPE: &str = "m.replace";
#[derive(Debug, Deserialize)]
struct MinimalEvent<'a> {
sender: &'a str,
event_id: &'a str,
#[serde(rename = "type")]
event_type: &'a str,
state_key: Option<&'a str>,
content: MinimalContent<'a>,
}
#[derive(Debug, Deserialize)]
struct MinimalContent<'a> {
#[serde(borrow, rename = "m.relates_to")]
relates_to: Option<MinimalRelatesTo<'a>>,
#[serde(rename = "m.new_content")]
new_content: Option<MinimalNewContent>,
}
#[derive(Debug, Deserialize)]
struct MinimalNewContent {}
#[derive(Debug, Deserialize)]
struct MinimalRelatesTo<'a> {
rel_type: Option<&'a str>,
event_id: Option<&'a str>,
}
let original_event = original_json.deserialize_as_unchecked::<MinimalEvent<'_>>()?;
let replacement_event = replacement_json.deserialize_as_unchecked::<MinimalEvent<'_>>()?;
if original_event.sender != replacement_event.sender {
return Err(EditValidityError::InvalidSender);
}
if let Some(relates_to) = replacement_event.content.relates_to {
if relates_to.rel_type != Some(REPLACEMENT_REL_TYPE)
|| relates_to.event_id != Some(original_event.event_id)
{
return Err(EditValidityError::NotReplacement);
}
} else {
return Err(EditValidityError::NotReplacement);
}
if original_event.event_type != replacement_event.event_type {
return Err(EditValidityError::MismatchContentType {
content_type: original_event.event_type.to_owned(),
replacement_type: replacement_event.event_type.to_owned(),
});
}
if original_event.state_key.is_some() || replacement_event.state_key.is_some() {
return Err(EditValidityError::StateKeyPresent);
}
if let Some(relates_to) = original_event.content.relates_to
&& relates_to.rel_type == Some(REPLACEMENT_REL_TYPE)
{
return Err(EditValidityError::OriginalEventIsReplacement);
}
if replacement_encryption_info.is_some() && replacement_event.content.new_content.is_none() {
return Err(EditValidityError::MissingNewContent);
}
if original_encryption_info.is_some() && replacement_encryption_info.is_none() {
return Err(EditValidityError::ReplacementNotEncrypted);
}
Ok(())
}