events: Update types according to changes in MSC3381

This commit is contained in:
Kévin Commaille 2023-01-05 14:46:17 +01:00 committed by Kévin Commaille
parent fbf99fcc53
commit 61c23491c6
6 changed files with 338 additions and 241 deletions

View File

@ -72,13 +72,13 @@ event_enum! {
"org.matrix.msc1767.message" => super::message, "org.matrix.msc1767.message" => super::message,
#[cfg(feature = "unstable-msc3381")] #[cfg(feature = "unstable-msc3381")]
#[ruma_enum(alias = "m.poll.start")] #[ruma_enum(alias = "m.poll.start")]
"org.matrix.msc3381.poll.start" => super::poll::start, "org.matrix.msc3381.v2.poll.start" => super::poll::start,
#[cfg(feature = "unstable-msc3381")] #[cfg(feature = "unstable-msc3381")]
#[ruma_enum(alias = "m.poll.response")] #[ruma_enum(alias = "m.poll.response")]
"org.matrix.msc3381.poll.response" => super::poll::response, "org.matrix.msc3381.v2.poll.response" => super::poll::response,
#[cfg(feature = "unstable-msc3381")] #[cfg(feature = "unstable-msc3381")]
#[ruma_enum(alias = "m.poll.end")] #[ruma_enum(alias = "m.poll.end")]
"org.matrix.msc3381.poll.end" => super::poll::end, "org.matrix.msc3381.v2.poll.end" => super::poll::end,
#[cfg(feature = "unstable-msc2677")] #[cfg(feature = "unstable-msc2677")]
"m.reaction" => super::reaction, "m.reaction" => super::reaction,
"m.room.encrypted" => super::room::encrypted, "m.room.encrypted" => super::room::encrypted,

View File

@ -271,8 +271,8 @@ pub struct TextRepresentation {
#[cfg(feature = "unstable-msc3554")] #[cfg(feature = "unstable-msc3554")]
#[serde( #[serde(
rename = "org.matrix.msc3554.lang", rename = "org.matrix.msc3554.lang",
default = "Text::default_lang", default = "TextRepresentation::default_lang",
skip_serializing_if = "Text::is_default_lang" skip_serializing_if = "TextRepresentation::is_default_lang"
)] )]
pub lang: String, pub lang: String,
} }
@ -316,10 +316,12 @@ impl TextRepresentation {
mime == "text/plain" mime == "text/plain"
} }
#[cfg(feature = "unstable-msc3554")]
fn default_lang() -> String { fn default_lang() -> String {
"en".to_owned() "en".to_owned()
} }
#[cfg(feature = "unstable-msc3554")]
fn is_default_lang(lang: &str) -> bool { fn is_default_lang(lang: &str) -> bool {
lang == "en" lang == "en"
} }

View File

@ -1,18 +1,43 @@
//! Types for the [`m.poll.end`] event. //! Types for the [`m.poll.end`] event.
use std::{
collections::{btree_map, BTreeMap},
ops::Deref,
};
use js_int::UInt;
use ruma_macros::EventContent; use ruma_macros::EventContent;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{events::relation::Reference, OwnedEventId}; use crate::{
events::{message::TextContentBlock, relation::Reference},
OwnedEventId,
};
/// The payload for a poll end event. /// The payload for a poll end event.
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)] #[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "org.matrix.msc3381.poll.end", alias = "m.poll.end", kind = MessageLike)] #[ruma_event(type = "org.matrix.msc3381.v2.poll.end", alias = "m.poll.end", kind = MessageLike)]
pub struct PollEndEventContent { pub struct PollEndEventContent {
/// The poll end content of the message. /// The text representation of the results.
#[serde(rename = "org.matrix.msc3381.poll.end", alias = "m.poll.end")] #[serde(rename = "org.matrix.msc1767.text")]
pub poll_end: PollEndContent, pub text: TextContentBlock,
/// The sender's perspective of the results.
#[serde(
rename = "org.matrix.msc3381.v2.poll.results",
skip_serializing_if = "Option::is_none"
)]
pub poll_results: Option<PollResultsContentBlock>,
/// Whether this message is automated.
#[cfg(feature = "unstable-msc3955")]
#[serde(
default,
skip_serializing_if = "crate::serde::is_default",
rename = "org.matrix.msc1767.automated"
)]
pub automated: bool,
/// Information about the poll start event this responds to. /// Information about the poll start event this responds to.
#[serde(rename = "m.relates_to")] #[serde(rename = "m.relates_to")]
@ -20,23 +45,63 @@ pub struct PollEndEventContent {
} }
impl PollEndEventContent { impl PollEndEventContent {
/// Creates a new `PollEndEventContent` that responds to the given poll start event ID, /// Creates a new `PollEndEventContent` with the given fallback representation and
/// with the given poll end content. /// that responds to the given poll start event ID.
pub fn new(poll_end: PollEndContent, poll_start_id: OwnedEventId) -> Self { pub fn new(text: TextContentBlock, poll_start_id: OwnedEventId) -> Self {
Self { poll_end, relates_to: Reference::new(poll_start_id) } Self {
text,
poll_results: None,
#[cfg(feature = "unstable-msc3955")]
automated: false,
relates_to: Reference::new(poll_start_id),
} }
} }
/// Poll end content. /// Creates a new `PollEndEventContent` with the given plain text fallback representation and
/// that responds to the given poll start event ID.
pub fn with_plain_text(plain_text: impl Into<String>, poll_start_id: OwnedEventId) -> Self {
Self {
text: TextContentBlock::plain(plain_text),
poll_results: None,
#[cfg(feature = "unstable-msc3955")]
automated: false,
relates_to: Reference::new(poll_start_id),
}
}
}
/// A block for the results of a poll.
/// ///
/// This is currently empty. /// This is a map of answer ID to number of votes.
#[derive(Clone, Debug, Default, Serialize, Deserialize)] #[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct PollEndContent {} pub struct PollResultsContentBlock(BTreeMap<String, UInt>);
impl PollEndContent { impl From<BTreeMap<String, UInt>> for PollResultsContentBlock {
/// Creates a new empty `PollEndContent`. fn from(value: BTreeMap<String, UInt>) -> Self {
pub fn new() -> Self { Self(value)
Self {} }
}
impl IntoIterator for PollResultsContentBlock {
type Item = (String, UInt);
type IntoIter = btree_map::IntoIter<String, UInt>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl FromIterator<(String, UInt)> for PollResultsContentBlock {
fn from_iter<T: IntoIterator<Item = (String, UInt)>>(iter: T) -> Self {
Self(BTreeMap::from_iter(iter))
}
}
impl Deref for PollResultsContentBlock {
type Target = BTreeMap<String, UInt>;
fn deref(&self) -> &Self::Target {
&self.0
} }
} }

View File

@ -1,5 +1,7 @@
//! Types for the [`m.poll.response`] event. //! Types for the [`m.poll.response`] event.
use std::{ops::Deref, vec};
use ruma_macros::EventContent; use ruma_macros::EventContent;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -8,11 +10,20 @@ use crate::{events::relation::Reference, OwnedEventId};
/// The payload for a poll response event. /// The payload for a poll response event.
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)] #[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "org.matrix.msc3381.poll.response", alias = "m.poll.response", kind = MessageLike)] #[ruma_event(type = "org.matrix.msc3381.v2.poll.response", alias = "m.poll.response", kind = MessageLike)]
pub struct PollResponseEventContent { pub struct PollResponseEventContent {
/// The poll response content of the message. /// The user's selection.
#[serde(rename = "org.matrix.msc3381.poll.response", alias = "m.poll.response")] #[serde(rename = "org.matrix.msc3381.v2.selections")]
pub poll_response: PollResponseContent, pub selections: SelectionsContentBlock,
/// Whether this message is automated.
#[cfg(feature = "unstable-msc3955")]
#[serde(
default,
skip_serializing_if = "crate::serde::is_default",
rename = "org.matrix.msc1767.automated"
)]
pub automated: bool,
/// Information about the poll start event this responds to. /// Information about the poll start event this responds to.
#[serde(rename = "m.relates_to")] #[serde(rename = "m.relates_to")]
@ -22,27 +33,53 @@ pub struct PollResponseEventContent {
impl PollResponseEventContent { impl PollResponseEventContent {
/// Creates a new `PollResponseEventContent` that responds to the given poll start event ID, /// Creates a new `PollResponseEventContent` that responds to the given poll start event ID,
/// with the given poll response content. /// with the given poll response content.
pub fn new(poll_response: PollResponseContent, poll_start_id: OwnedEventId) -> Self { pub fn new(selections: SelectionsContentBlock, poll_start_id: OwnedEventId) -> Self {
Self { poll_response, relates_to: Reference::new(poll_start_id) } Self {
selections,
#[cfg(feature = "unstable-msc3955")]
automated: false,
relates_to: Reference::new(poll_start_id),
}
} }
} }
/// Poll response content. /// A block for selections content.
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct PollResponseContent { pub struct SelectionsContentBlock(Vec<String>);
/// The IDs of the selected answers of the poll.
/// impl SelectionsContentBlock {
/// It should be truncated to `max_selections` from the related poll start event. /// Whether this `SelectionsContentBlock` is empty.
/// pub fn is_empty(&self) -> bool {
/// If this is an empty array or includes unknown IDs, this vote should be considered as self.0.is_empty()
/// spoiled. }
pub answers: Vec<String>,
} }
impl PollResponseContent { impl From<Vec<String>> for SelectionsContentBlock {
/// Creates a new `PollResponseContent` with the given answers. fn from(value: Vec<String>) -> Self {
pub fn new(answers: Vec<String>) -> Self { Self(value)
Self { answers } }
}
impl IntoIterator for SelectionsContentBlock {
type Item = String;
type IntoIter = vec::IntoIter<String>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl FromIterator<String> for SelectionsContentBlock {
fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
Self(Vec::from_iter(iter))
}
}
impl Deref for SelectionsContentBlock {
type Target = [String];
fn deref(&self) -> &Self::Target {
&self.0
} }
} }

View File

@ -1,5 +1,7 @@
//! Types for the [`m.poll.start`] event. //! Types for the [`m.poll.start`] event.
use std::ops::Deref;
use js_int::{uint, UInt}; use js_int::{uint, UInt};
use ruma_macros::EventContent; use ruma_macros::EventContent;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -13,37 +15,59 @@ use crate::{events::message::TextContentBlock, serde::StringEnum, PrivOwnedStr};
/// The payload for a poll start event. /// The payload for a poll start event.
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)] #[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "org.matrix.msc3381.poll.start", alias = "m.poll.start", kind = MessageLike)] #[ruma_event(type = "org.matrix.msc3381.v2.poll.start", alias = "m.poll.start", kind = MessageLike)]
pub struct PollStartEventContent { pub struct PollStartEventContent {
/// The poll start content of the message. /// The poll content of the message.
#[serde(rename = "org.matrix.msc3381.poll.start", alias = "m.poll.start")] #[serde(rename = "org.matrix.msc3381.v2.poll")]
pub poll_start: PollStartContent, pub poll: PollContentBlock,
/// Optional fallback text representation of the message, for clients that don't support polls. /// Text representation of the message, for clients that don't support polls.
#[serde( #[serde(rename = "org.matrix.msc1767.text")]
rename = "org.matrix.msc1767.text",
default,
skip_serializing_if = "TextContentBlock::is_empty"
)]
pub text: TextContentBlock, pub text: TextContentBlock,
/// Whether this message is automated.
#[cfg(feature = "unstable-msc3955")]
#[serde(
default,
skip_serializing_if = "crate::serde::is_default",
rename = "org.matrix.msc1767.automated"
)]
pub automated: bool,
} }
impl PollStartEventContent { impl PollStartEventContent {
/// Creates a new `PollStartEventContent` with the given poll start content. /// Creates a new `PollStartEventContent` with the given fallback representation and poll
pub fn new(poll_start: PollStartContent) -> Self { /// content.
Self { poll_start, text: Default::default() } pub fn new(text: TextContentBlock, poll: PollContentBlock) -> Self {
Self {
poll,
text,
#[cfg(feature = "unstable-msc3955")]
automated: false,
} }
} }
/// Poll start content. /// Creates a new `PollStartEventContent` with the given plain text fallback
/// representation and poll content.
pub fn with_plain_text(plain_text: impl Into<String>, poll: PollContentBlock) -> Self {
Self {
poll,
text: TextContentBlock::plain(plain_text),
#[cfg(feature = "unstable-msc3955")]
automated: false,
}
}
}
/// A block for poll content.
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct PollStartContent { pub struct PollContentBlock {
/// The question of the poll. /// The question of the poll.
pub question: PollQuestion, pub question: PollQuestion,
/// The kind of the poll. /// The kind of the poll.
#[serde(default)] #[serde(default, skip_serializing_if = "crate::serde::is_default")]
pub kind: PollKind, pub kind: PollKind,
/// The maximum number of responses a user is able to select. /// The maximum number of responses a user is able to select.
@ -52,8 +76,8 @@ pub struct PollStartContent {
/// ///
/// Defaults to `1`. /// Defaults to `1`.
#[serde( #[serde(
default = "PollStartContent::default_max_selections", default = "PollContentBlock::default_max_selections",
skip_serializing_if = "PollStartContent::max_selections_is_default" skip_serializing_if = "PollContentBlock::max_selections_is_default"
)] )]
pub max_selections: UInt, pub max_selections: UInt,
@ -61,12 +85,12 @@ pub struct PollStartContent {
pub answers: PollAnswers, pub answers: PollAnswers,
} }
impl PollStartContent { impl PollContentBlock {
/// Creates a new `PollStartContent` with the given question, kind, and answers. /// Creates a new `PollStartContent` with the given question and answers.
pub fn new(question: TextContentBlock, kind: PollKind, answers: PollAnswers) -> Self { pub fn new(question: TextContentBlock, answers: PollAnswers) -> Self {
Self { Self {
question: question.into(), question: question.into(),
kind, kind: Default::default(),
max_selections: Self::default_max_selections(), max_selections: Self::default_max_selections(),
answers, answers,
} }
@ -103,11 +127,11 @@ impl From<TextContentBlock> for PollQuestion {
pub enum PollKind { pub enum PollKind {
/// The results are revealed once the poll is closed. /// The results are revealed once the poll is closed.
#[default] #[default]
#[ruma_enum(rename = "org.matrix.msc3381.poll.undisclosed", alias = "m.poll.undisclosed")] #[ruma_enum(rename = "org.matrix.msc3381.v2.undisclosed")]
Undisclosed, Undisclosed,
/// The votes are visible up until and including when the poll is closed. /// The votes are visible up until and including when the poll is closed.
#[ruma_enum(rename = "org.matrix.msc3381.poll.disclosed", alias = "m.poll.disclosed")] #[ruma_enum(rename = "org.matrix.msc3381.v2.disclosed")]
Disclosed, Disclosed,
#[doc(hidden)] #[doc(hidden)]
@ -129,11 +153,6 @@ impl PollAnswers {
/// The largest number of values contained in a `PollAnswers`. /// The largest number of values contained in a `PollAnswers`.
pub const MAX_LENGTH: usize = 20; pub const MAX_LENGTH: usize = 20;
/// The answers of this `PollAnswers`.
pub fn answers(&self) -> &[PollAnswer] {
&self.0
}
} }
/// An error encountered when trying to convert to a `PollAnswers`. /// An error encountered when trying to convert to a `PollAnswers`.
@ -170,6 +189,14 @@ impl TryFrom<&[PollAnswer]> for PollAnswers {
} }
} }
impl Deref for PollAnswers {
type Target = [PollAnswer];
fn deref(&self) -> &Self::Target {
&self.0
}
}
/// Poll answer. /// Poll answer.
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
@ -177,6 +204,7 @@ pub struct PollAnswer {
/// The ID of the answer. /// The ID of the answer.
/// ///
/// This must be unique among the answers of a poll. /// This must be unique among the answers of a poll.
#[serde(rename = "org.matrix.msc3381.v2.id")]
pub id: String, pub id: String,
/// The text representation of the answer. /// The text representation of the answer.

View File

@ -1,17 +1,18 @@
#![cfg(feature = "unstable-msc3381")] #![cfg(feature = "unstable-msc3381")]
use std::collections::BTreeMap;
use assert_matches::assert_matches; use assert_matches::assert_matches;
use assign::assign;
use js_int::uint; use js_int::uint;
use ruma_common::{ use ruma_common::{
event_id, event_id,
events::{ events::{
message::TextContentBlock, message::TextContentBlock,
poll::{ poll::{
end::{PollEndContent, PollEndEventContent}, end::PollEndEventContent,
response::{PollResponseContent, PollResponseEventContent}, response::PollResponseEventContent,
start::{ start::{
PollAnswer, PollAnswers, PollAnswersError, PollKind, PollStartContent, PollAnswer, PollAnswers, PollAnswersError, PollContentBlock, PollKind,
PollStartEventContent, PollStartEventContent,
}, },
}, },
@ -24,43 +25,43 @@ use serde_json::{from_value as from_json_value, json, to_value as to_json_value}
#[test] #[test]
fn poll_answers_deserialization_valid() { fn poll_answers_deserialization_valid() {
let json_data = json!([ let json_data = json!([
{ "id": "aaa", "org.matrix.msc1767.text": [{ "body": "First answer" }] }, { "org.matrix.msc3381.v2.id": "aaa", "org.matrix.msc1767.text": [{ "body": "First answer" }] },
{ "id": "bbb", "org.matrix.msc1767.text": [{ "body": "Second answer" }] }, { "org.matrix.msc3381.v2.id": "bbb", "org.matrix.msc1767.text": [{ "body": "Second answer" }] },
]); ]);
let answers = from_json_value::<PollAnswers>(json_data).unwrap(); let answers = from_json_value::<PollAnswers>(json_data).unwrap();
assert_eq!(answers.answers().len(), 2); assert_eq!(answers.len(), 2);
} }
#[test] #[test]
fn poll_answers_deserialization_truncate() { fn poll_answers_deserialization_truncate() {
let json_data = json!([ let json_data = json!([
{ "id": "aaa", "org.matrix.msc1767.text": [{ "body": "1st answer" }] }, { "org.matrix.msc3381.v2.id": "aaa", "org.matrix.msc1767.text": [{ "body": "1st answer" }] },
{ "id": "bbb", "org.matrix.msc1767.text": [{ "body": "2nd answer" }] }, { "org.matrix.msc3381.v2.id": "bbb", "org.matrix.msc1767.text": [{ "body": "2nd answer" }] },
{ "id": "ccc", "org.matrix.msc1767.text": [{ "body": "3rd answer" }] }, { "org.matrix.msc3381.v2.id": "ccc", "org.matrix.msc1767.text": [{ "body": "3rd answer" }] },
{ "id": "ddd", "org.matrix.msc1767.text": [{ "body": "4th answer" }] }, { "org.matrix.msc3381.v2.id": "ddd", "org.matrix.msc1767.text": [{ "body": "4th answer" }] },
{ "id": "eee", "org.matrix.msc1767.text": [{ "body": "5th answer" }] }, { "org.matrix.msc3381.v2.id": "eee", "org.matrix.msc1767.text": [{ "body": "5th answer" }] },
{ "id": "fff", "org.matrix.msc1767.text": [{ "body": "6th answer" }] }, { "org.matrix.msc3381.v2.id": "fff", "org.matrix.msc1767.text": [{ "body": "6th answer" }] },
{ "id": "ggg", "org.matrix.msc1767.text": [{ "body": "7th answer" }] }, { "org.matrix.msc3381.v2.id": "ggg", "org.matrix.msc1767.text": [{ "body": "7th answer" }] },
{ "id": "hhh", "org.matrix.msc1767.text": [{ "body": "8th answer" }] }, { "org.matrix.msc3381.v2.id": "hhh", "org.matrix.msc1767.text": [{ "body": "8th answer" }] },
{ "id": "iii", "org.matrix.msc1767.text": [{ "body": "9th answer" }] }, { "org.matrix.msc3381.v2.id": "iii", "org.matrix.msc1767.text": [{ "body": "9th answer" }] },
{ "id": "jjj", "org.matrix.msc1767.text": [{ "body": "10th answer" }] }, { "org.matrix.msc3381.v2.id": "jjj", "org.matrix.msc1767.text": [{ "body": "10th answer" }] },
{ "id": "kkk", "org.matrix.msc1767.text": [{ "body": "11th answer" }] }, { "org.matrix.msc3381.v2.id": "kkk", "org.matrix.msc1767.text": [{ "body": "11th answer" }] },
{ "id": "lll", "org.matrix.msc1767.text": [{ "body": "12th answer" }] }, { "org.matrix.msc3381.v2.id": "lll", "org.matrix.msc1767.text": [{ "body": "12th answer" }] },
{ "id": "mmm", "org.matrix.msc1767.text": [{ "body": "13th answer" }] }, { "org.matrix.msc3381.v2.id": "mmm", "org.matrix.msc1767.text": [{ "body": "13th answer" }] },
{ "id": "nnn", "org.matrix.msc1767.text": [{ "body": "14th answer" }] }, { "org.matrix.msc3381.v2.id": "nnn", "org.matrix.msc1767.text": [{ "body": "14th answer" }] },
{ "id": "ooo", "org.matrix.msc1767.text": [{ "body": "15th answer" }] }, { "org.matrix.msc3381.v2.id": "ooo", "org.matrix.msc1767.text": [{ "body": "15th answer" }] },
{ "id": "ppp", "org.matrix.msc1767.text": [{ "body": "16th answer" }] }, { "org.matrix.msc3381.v2.id": "ppp", "org.matrix.msc1767.text": [{ "body": "16th answer" }] },
{ "id": "qqq", "org.matrix.msc1767.text": [{ "body": "17th answer" }] }, { "org.matrix.msc3381.v2.id": "qqq", "org.matrix.msc1767.text": [{ "body": "17th answer" }] },
{ "id": "rrr", "org.matrix.msc1767.text": [{ "body": "18th answer" }] }, { "org.matrix.msc3381.v2.id": "rrr", "org.matrix.msc1767.text": [{ "body": "18th answer" }] },
{ "id": "sss", "org.matrix.msc1767.text": [{ "body": "19th answer" }] }, { "org.matrix.msc3381.v2.id": "sss", "org.matrix.msc1767.text": [{ "body": "19th answer" }] },
{ "id": "ttt", "org.matrix.msc1767.text": [{ "body": "20th answer" }] }, { "org.matrix.msc3381.v2.id": "ttt", "org.matrix.msc1767.text": [{ "body": "20th answer" }] },
{ "id": "uuu", "org.matrix.msc1767.text": [{ "body": "21th answer" }] }, { "org.matrix.msc3381.v2.id": "uuu", "org.matrix.msc1767.text": [{ "body": "21th answer" }] },
{ "id": "vvv", "org.matrix.msc1767.text": [{ "body": "22th answer" }] }, { "org.matrix.msc3381.v2.id": "vvv", "org.matrix.msc1767.text": [{ "body": "22th answer" }] },
]); ]);
let answers = from_json_value::<PollAnswers>(json_data).unwrap(); let answers = from_json_value::<PollAnswers>(json_data).unwrap();
assert_eq!(answers.answers().len(), 20); assert_eq!(answers.len(), 20);
} }
#[test] #[test]
@ -74,40 +75,10 @@ fn poll_answers_deserialization_not_enough() {
#[test] #[test]
fn start_content_serialization() { fn start_content_serialization() {
let event_content = PollStartEventContent::new(PollStartContent::new( let event_content = PollStartEventContent::with_plain_text(
"How's the weather?\n1. Not bad…\n2. Fine.\n3. Amazing!",
PollContentBlock::new(
TextContentBlock::plain("How's the weather?"), TextContentBlock::plain("How's the weather?"),
PollKind::Undisclosed,
vec![
PollAnswer::new("not-bad".to_owned(), TextContentBlock::plain("Not bad…")),
PollAnswer::new("fine".to_owned(), TextContentBlock::plain("Fine.")),
PollAnswer::new("amazing".to_owned(), TextContentBlock::plain("Amazing!")),
]
.try_into()
.unwrap(),
));
assert_eq!(
to_json_value(&event_content).unwrap(),
json!({
"org.matrix.msc3381.poll.start": {
"question": { "org.matrix.msc1767.text": [{ "body": "How's the weather?" }] },
"kind": "org.matrix.msc3381.poll.undisclosed",
"answers": [
{ "id": "not-bad", "org.matrix.msc1767.text": [{ "body": "Not bad…" }] },
{ "id": "fine", "org.matrix.msc1767.text": [{ "body": "Fine." }] },
{ "id": "amazing", "org.matrix.msc1767.text": [{ "body": "Amazing!" }] },
],
},
})
);
}
#[test]
fn start_event_serialization() {
let content = PollStartEventContent::new(assign!(
PollStartContent::new(
TextContentBlock::plain("How's the weather?"),
PollKind::Disclosed,
vec![ vec![
PollAnswer::new("not-bad".to_owned(), TextContentBlock::plain("Not bad…")), PollAnswer::new("not-bad".to_owned(), TextContentBlock::plain("Not bad…")),
PollAnswer::new("fine".to_owned(), TextContentBlock::plain("Fine.")), PollAnswer::new("fine".to_owned(), TextContentBlock::plain("Fine.")),
@ -116,20 +87,59 @@ fn start_event_serialization() {
.try_into() .try_into()
.unwrap(), .unwrap(),
), ),
{ max_selections: uint!(2) } );
));
assert_eq!(
to_json_value(&event_content).unwrap(),
json!({
"org.matrix.msc1767.text": [
{ "body": "How's the weather?\n1. Not bad…\n2. Fine.\n3. Amazing!" }
],
"org.matrix.msc3381.v2.poll": {
"question": { "org.matrix.msc1767.text": [{ "body": "How's the weather?" }] },
"answers": [
{ "org.matrix.msc3381.v2.id": "not-bad", "org.matrix.msc1767.text": [{ "body": "Not bad…" }] },
{ "org.matrix.msc3381.v2.id": "fine", "org.matrix.msc1767.text": [{ "body": "Fine." }] },
{ "org.matrix.msc3381.v2.id": "amazing", "org.matrix.msc1767.text": [{ "body": "Amazing!" }] },
],
},
})
);
}
#[test]
fn start_event_serialization() {
let mut poll = PollContentBlock::new(
TextContentBlock::plain("How's the weather?"),
vec![
PollAnswer::new("not-bad".to_owned(), TextContentBlock::plain("Not bad…")),
PollAnswer::new("fine".to_owned(), TextContentBlock::plain("Fine.")),
PollAnswer::new("amazing".to_owned(), TextContentBlock::plain("Amazing!")),
]
.try_into()
.unwrap(),
);
poll.kind = PollKind::Disclosed;
poll.max_selections = uint!(2);
let content = PollStartEventContent::with_plain_text(
"How's the weather?\n1. Not bad…\n2. Fine.\n3. Amazing!",
poll,
);
assert_eq!( assert_eq!(
to_json_value(&content).unwrap(), to_json_value(&content).unwrap(),
json!({ json!({
"org.matrix.msc3381.poll.start": { "org.matrix.msc1767.text": [
{ "body": "How's the weather?\n1. Not bad…\n2. Fine.\n3. Amazing!" }
],
"org.matrix.msc3381.v2.poll": {
"question": { "org.matrix.msc1767.text": [{ "body": "How's the weather?" }] }, "question": { "org.matrix.msc1767.text": [{ "body": "How's the weather?" }] },
"kind": "org.matrix.msc3381.poll.disclosed", "kind": "org.matrix.msc3381.v2.disclosed",
"max_selections": 2, "max_selections": 2,
"answers": [ "answers": [
{ "id": "not-bad", "org.matrix.msc1767.text": [{ "body": "Not bad…" }] }, { "org.matrix.msc3381.v2.id": "not-bad", "org.matrix.msc1767.text": [{ "body": "Not bad…" }] },
{ "id": "fine", "org.matrix.msc1767.text": [{ "body": "Fine." }] }, { "org.matrix.msc3381.v2.id": "fine", "org.matrix.msc1767.text": [{ "body": "Fine." }] },
{ "id": "amazing", "org.matrix.msc1767.text": [{ "body": "Amazing!" }] }, { "org.matrix.msc3381.v2.id": "amazing", "org.matrix.msc1767.text": [{ "body": "Amazing!" }] },
] ]
}, },
}) })
@ -140,14 +150,16 @@ fn start_event_serialization() {
fn start_event_deserialization() { fn start_event_deserialization() {
let json_data = json!({ let json_data = json!({
"content": { "content": {
"org.matrix.msc3381.poll.start": { "org.matrix.msc1767.text": [
{ "body": "How's the weather?\n1. Not bad…\n2. Fine.\n3. Amazing!" }
],
"org.matrix.msc3381.v2.poll": {
"question": { "org.matrix.msc1767.text": [{ "body": "How's the weather?" }] }, "question": { "org.matrix.msc1767.text": [{ "body": "How's the weather?" }] },
"kind": "org.matrix.msc3381.poll.undisclosed",
"max_selections": 2, "max_selections": 2,
"answers": [ "answers": [
{ "id": "not-bad", "org.matrix.msc1767.text": [{ "body": "Not bad…" }] }, { "org.matrix.msc3381.v2.id": "not-bad", "org.matrix.msc1767.text": [{ "body": "Not bad…" }] },
{ "id": "fine", "org.matrix.msc1767.text": [{ "body": "Fine." }] }, { "org.matrix.msc3381.v2.id": "fine", "org.matrix.msc1767.text": [{ "body": "Fine." }] },
{ "id": "amazing", "org.matrix.msc1767.text": [{ "body": "Amazing!" }] }, { "org.matrix.msc3381.v2.id": "amazing", "org.matrix.msc1767.text": [{ "body": "Amazing!" }] },
] ]
}, },
}, },
@ -155,7 +167,7 @@ fn start_event_deserialization() {
"origin_server_ts": 134_829_848, "origin_server_ts": 134_829_848,
"room_id": "!roomid:notareal.hs", "room_id": "!roomid:notareal.hs",
"sender": "@user:notareal.hs", "sender": "@user:notareal.hs",
"type": "org.matrix.msc3381.poll.start", "type": "org.matrix.msc3381.v2.poll.start",
}); });
let event = from_json_value::<AnyMessageLikeEvent>(json_data).unwrap(); let event = from_json_value::<AnyMessageLikeEvent>(json_data).unwrap();
@ -163,11 +175,15 @@ fn start_event_deserialization() {
event, event,
AnyMessageLikeEvent::PollStart(MessageLikeEvent::Original(message_event)) => message_event AnyMessageLikeEvent::PollStart(MessageLikeEvent::Original(message_event)) => message_event
); );
let poll_start = message_event.content.poll_start; assert_eq!(
assert_eq!(poll_start.question.text[0].body, "How's the weather?"); message_event.content.text[0].body,
assert_eq!(poll_start.kind, PollKind::Undisclosed); "How's the weather?\n1. Not bad…\n2. Fine.\n3. Amazing!"
assert_eq!(poll_start.max_selections, uint!(2)); );
let answers = poll_start.answers.answers(); let poll = message_event.content.poll;
assert_eq!(poll.question.text[0].body, "How's the weather?");
assert_eq!(poll.kind, PollKind::Undisclosed);
assert_eq!(poll.max_selections, uint!(2));
let answers = poll.answers;
assert_eq!(answers.len(), 3); assert_eq!(answers.len(), 3);
assert_eq!(answers[0].id, "not-bad"); assert_eq!(answers[0].id, "not-bad");
assert_eq!(answers[0].text[0].body, "Not bad…"); assert_eq!(answers[0].text[0].body, "Not bad…");
@ -180,16 +196,14 @@ fn start_event_deserialization() {
#[test] #[test]
fn response_content_serialization() { fn response_content_serialization() {
let event_content = PollResponseEventContent::new( let event_content = PollResponseEventContent::new(
PollResponseContent::new(vec!["my-answer".to_owned()]), vec!["my-answer".to_owned()].into(),
event_id!("$related_event:notareal.hs").to_owned(), event_id!("$related_event:notareal.hs").to_owned(),
); );
assert_eq!( assert_eq!(
to_json_value(&event_content).unwrap(), to_json_value(&event_content).unwrap(),
json!({ json!({
"org.matrix.msc3381.poll.response": { "org.matrix.msc3381.v2.selections": ["my-answer"],
"answers": ["my-answer"],
},
"m.relates_to": { "m.relates_to": {
"rel_type": "m.reference", "rel_type": "m.reference",
"event_id": "$related_event:notareal.hs", "event_id": "$related_event:notareal.hs",
@ -201,16 +215,14 @@ fn response_content_serialization() {
#[test] #[test]
fn response_event_serialization() { fn response_event_serialization() {
let content = PollResponseEventContent::new( let content = PollResponseEventContent::new(
PollResponseContent::new(vec!["first-answer".to_owned(), "second-answer".to_owned()]), vec!["first-answer".to_owned(), "second-answer".to_owned()].into(),
event_id!("$related_event:notareal.hs").to_owned(), event_id!("$related_event:notareal.hs").to_owned(),
); );
assert_eq!( assert_eq!(
to_json_value(&content).unwrap(), to_json_value(&content).unwrap(),
json!({ json!({
"org.matrix.msc3381.poll.response": { "org.matrix.msc3381.v2.selections": ["first-answer", "second-answer"],
"answers": ["first-answer", "second-answer"],
},
"m.relates_to": { "m.relates_to": {
"rel_type": "m.reference", "rel_type": "m.reference",
"event_id": "$related_event:notareal.hs", "event_id": "$related_event:notareal.hs",
@ -220,12 +232,10 @@ fn response_event_serialization() {
} }
#[test] #[test]
fn response_event_unstable_deserialization() { fn response_event_deserialization() {
let json_data = json!({ let json_data = json!({
"content": { "content": {
"org.matrix.msc3381.poll.response": { "org.matrix.msc3381.v2.selections": ["my-answer"],
"answers": ["my-answer"],
},
"m.relates_to": { "m.relates_to": {
"rel_type": "m.reference", "rel_type": "m.reference",
"event_id": "$related_event:notareal.hs", "event_id": "$related_event:notareal.hs",
@ -235,7 +245,7 @@ fn response_event_unstable_deserialization() {
"origin_server_ts": 134_829_848, "origin_server_ts": 134_829_848,
"room_id": "!roomid:notareal.hs", "room_id": "!roomid:notareal.hs",
"sender": "@user:notareal.hs", "sender": "@user:notareal.hs",
"type": "org.matrix.msc3381.poll.response", "type": "org.matrix.msc3381.v2.poll.response",
}); });
let event = from_json_value::<AnyMessageLikeEvent>(json_data).unwrap(); let event = from_json_value::<AnyMessageLikeEvent>(json_data).unwrap();
@ -244,45 +254,9 @@ fn response_event_unstable_deserialization() {
AnyMessageLikeEvent::PollResponse(MessageLikeEvent::Original(message_event)) AnyMessageLikeEvent::PollResponse(MessageLikeEvent::Original(message_event))
=> message_event => message_event
); );
let answers = message_event.content.poll_response.answers; let selections = message_event.content.selections;
assert_eq!(answers.len(), 1); assert_eq!(selections.len(), 1);
assert_eq!(answers[0], "my-answer"); assert_eq!(selections[0], "my-answer");
let event_id = assert_matches!(
message_event.content.relates_to,
Reference { event_id, .. } => event_id
);
assert_eq!(event_id, "$related_event:notareal.hs");
}
#[test]
fn response_event_stable_deserialization() {
let json_data = json!({
"content": {
"m.poll.response": {
"answers": ["first-answer", "second-answer"],
},
"m.relates_to": {
"rel_type": "m.reference",
"event_id": "$related_event:notareal.hs",
}
},
"event_id": "$event:notareal.hs",
"origin_server_ts": 134_829_848,
"room_id": "!roomid:notareal.hs",
"sender": "@user:notareal.hs",
"type": "m.poll.response",
});
let event = from_json_value::<AnyMessageLikeEvent>(json_data).unwrap();
let message_event = assert_matches!(
event,
AnyMessageLikeEvent::PollResponse(MessageLikeEvent::Original(message_event))
=> message_event
);
let answers = message_event.content.poll_response.answers;
assert_eq!(answers.len(), 2);
assert_eq!(answers[0], "first-answer");
assert_eq!(answers[1], "second-answer");
let event_id = assert_matches!( let event_id = assert_matches!(
message_event.content.relates_to, message_event.content.relates_to,
Reference { event_id, .. } => event_id Reference { event_id, .. } => event_id
@ -292,15 +266,17 @@ fn response_event_stable_deserialization() {
#[test] #[test]
fn end_content_serialization() { fn end_content_serialization() {
let event_content = PollEndEventContent::new( let event_content = PollEndEventContent::with_plain_text(
PollEndContent::new(), "The poll has closed. Top answer: Amazing!",
event_id!("$related_event:notareal.hs").to_owned(), event_id!("$related_event:notareal.hs").to_owned(),
); );
assert_eq!( assert_eq!(
to_json_value(&event_content).unwrap(), to_json_value(&event_content).unwrap(),
json!({ json!({
"org.matrix.msc3381.poll.end": {}, "org.matrix.msc1767.text": [
{ "body": "The poll has closed. Top answer: Amazing!" }
],
"m.relates_to": { "m.relates_to": {
"rel_type": "m.reference", "rel_type": "m.reference",
"event_id": "$related_event:notareal.hs", "event_id": "$related_event:notareal.hs",
@ -311,15 +287,30 @@ fn end_content_serialization() {
#[test] #[test]
fn end_event_serialization() { fn end_event_serialization() {
let content = PollEndEventContent::new( let mut content = PollEndEventContent::with_plain_text(
PollEndContent::new(), "The poll has closed. Top answer: Amazing!",
event_id!("$related_event:notareal.hs").to_owned(), event_id!("$related_event:notareal.hs").to_owned(),
); );
content.poll_results = Some(
BTreeMap::from([
("not-bad".to_owned(), uint!(1)),
("fine".to_owned(), uint!(5)),
("amazing".to_owned(), uint!(14)),
])
.into(),
);
assert_eq!( assert_eq!(
to_json_value(&content).unwrap(), to_json_value(&content).unwrap(),
json!({ json!({
"org.matrix.msc3381.poll.end": {}, "org.matrix.msc1767.text": [
{ "body": "The poll has closed. Top answer: Amazing!" },
],
"org.matrix.msc3381.v2.poll.results": {
"not-bad": 1,
"fine": 5,
"amazing": 14,
},
"m.relates_to": { "m.relates_to": {
"rel_type": "m.reference", "rel_type": "m.reference",
"event_id": "$related_event:notareal.hs", "event_id": "$related_event:notareal.hs",
@ -329,10 +320,12 @@ fn end_event_serialization() {
} }
#[test] #[test]
fn end_event_unstable_deserialization() { fn end_event_deserialization() {
let json_data = json!({ let json_data = json!({
"content": { "content": {
"org.matrix.msc3381.poll.end": {}, "org.matrix.msc1767.text": [
{ "body": "The poll has closed. Top answer: Amazing!" },
],
"m.relates_to": { "m.relates_to": {
"rel_type": "m.reference", "rel_type": "m.reference",
"event_id": "$related_event:notareal.hs", "event_id": "$related_event:notareal.hs",
@ -342,36 +335,7 @@ fn end_event_unstable_deserialization() {
"origin_server_ts": 134_829_848, "origin_server_ts": 134_829_848,
"room_id": "!roomid:notareal.hs", "room_id": "!roomid:notareal.hs",
"sender": "@user:notareal.hs", "sender": "@user:notareal.hs",
"type": "org.matrix.msc3381.poll.end", "type": "org.matrix.msc3381.v2.poll.end",
});
let event = from_json_value::<AnyMessageLikeEvent>(json_data).unwrap();
let message_event = assert_matches!(
event,
AnyMessageLikeEvent::PollEnd(MessageLikeEvent::Original(message_event)) => message_event
);
let event_id = assert_matches!(
message_event.content.relates_to,
Reference { event_id, .. } => event_id
);
assert_eq!(event_id, "$related_event:notareal.hs");
}
#[test]
fn end_event_stable_deserialization() {
let json_data = json!({
"content": {
"m.poll.end": {},
"m.relates_to": {
"rel_type": "m.reference",
"event_id": "$related_event:notareal.hs",
}
},
"event_id": "$event:notareal.hs",
"origin_server_ts": 134_829_848,
"room_id": "!roomid:notareal.hs",
"sender": "@user:notareal.hs",
"type": "m.poll.end",
}); });
let event = from_json_value::<AnyMessageLikeEvent>(json_data).unwrap(); let event = from_json_value::<AnyMessageLikeEvent>(json_data).unwrap();
@ -379,6 +343,7 @@ fn end_event_stable_deserialization() {
event, event,
AnyMessageLikeEvent::PollEnd(MessageLikeEvent::Original(message_event)) => message_event AnyMessageLikeEvent::PollEnd(MessageLikeEvent::Original(message_event)) => message_event
); );
assert_eq!(message_event.content.text[0].body, "The poll has closed. Top answer: Amazing!");
let event_id = assert_matches!( let event_id = assert_matches!(
message_event.content.relates_to, message_event.content.relates_to,
Reference { event_id, .. } => event_id Reference { event_id, .. } => event_id