use std::ops::Deref;
use js_int::UInt;
use ruma_macros::EventContent;
use serde::{Deserialize, Serialize};
mod content_serde;
mod unstable_poll_answers_serde;
mod unstable_poll_kind_serde;
use ruma_common::{MilliSecondsSinceUnixEpoch, OwnedEventId};
use self::unstable_poll_answers_serde::UnstablePollAnswersDeHelper;
use super::{
compile_unstable_poll_results, generate_poll_end_fallback_text,
start::{PollAnswers, PollAnswersError, PollContentBlock, PollKind},
unstable_end::UnstablePollEndEventContent,
PollResponseData,
};
use crate::{
relation::Replacement, room::message::RelationWithoutReplacement, EventContent,
MessageLikeEventContent, MessageLikeEventType, RedactContent, RedactedMessageLikeEventContent,
StaticEventContent,
};
#[derive(Clone, Debug, Serialize, EventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "org.matrix.msc3381.poll.start", kind = MessageLike, custom_redacted)]
#[serde(untagged)]
#[allow(clippy::large_enum_variant)]
pub enum UnstablePollStartEventContent {
New(NewUnstablePollStartEventContent),
Replacement(ReplacementUnstablePollStartEventContent),
}
impl UnstablePollStartEventContent {
pub fn poll_start(&self) -> &UnstablePollStartContentBlock {
match self {
Self::New(c) => &c.poll_start,
Self::Replacement(c) => &c.relates_to.new_content.poll_start,
}
}
}
impl RedactContent for UnstablePollStartEventContent {
type Redacted = RedactedUnstablePollStartEventContent;
fn redact(self, _version: &crate::RoomVersionId) -> Self::Redacted {
RedactedUnstablePollStartEventContent::default()
}
}
impl From<NewUnstablePollStartEventContent> for UnstablePollStartEventContent {
fn from(value: NewUnstablePollStartEventContent) -> Self {
Self::New(value)
}
}
impl From<ReplacementUnstablePollStartEventContent> for UnstablePollStartEventContent {
fn from(value: ReplacementUnstablePollStartEventContent) -> Self {
Self::Replacement(value)
}
}
impl OriginalSyncUnstablePollStartEvent {
pub fn compile_results<'a>(
&'a self,
responses: impl IntoIterator<Item = PollResponseData<'a>>,
) -> UnstablePollEndEventContent {
let poll_start = self.content.poll_start();
let full_results = compile_unstable_poll_results(
poll_start,
responses,
Some(MilliSecondsSinceUnixEpoch::now()),
);
let results =
full_results.into_iter().map(|(id, users)| (id, users.len())).collect::<Vec<_>>();
let answers =
poll_start.answers.iter().map(|a| (a.id.as_str(), a.text.as_str())).collect::<Vec<_>>();
let plain_text = generate_poll_end_fallback_text(&answers, results.into_iter());
UnstablePollEndEventContent::new(plain_text, self.event_id.clone())
}
}
#[derive(Clone, Debug, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct NewUnstablePollStartEventContent {
#[serde(rename = "org.matrix.msc3381.poll.start")]
pub poll_start: UnstablePollStartContentBlock,
#[serde(rename = "org.matrix.msc1767.text")]
pub text: Option<String>,
#[serde(rename = "m.relates_to", skip_serializing_if = "Option::is_none")]
pub relates_to: Option<RelationWithoutReplacement>,
}
impl NewUnstablePollStartEventContent {
pub fn new(poll_start: UnstablePollStartContentBlock) -> Self {
Self { poll_start, text: None, relates_to: None }
}
pub fn plain_text(text: impl Into<String>, poll_start: UnstablePollStartContentBlock) -> Self {
Self { poll_start, text: Some(text.into()), relates_to: None }
}
}
impl EventContent for NewUnstablePollStartEventContent {
type EventType = MessageLikeEventType;
fn event_type(&self) -> Self::EventType {
MessageLikeEventType::UnstablePollStart
}
}
impl StaticEventContent for NewUnstablePollStartEventContent {
const TYPE: &'static str = "org.matrix.msc3381.poll.start";
}
impl MessageLikeEventContent for NewUnstablePollStartEventContent {}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct NewUnstablePollStartEventContentWithoutRelation {
#[serde(rename = "org.matrix.msc3381.poll.start")]
pub poll_start: UnstablePollStartContentBlock,
#[serde(rename = "org.matrix.msc1767.text")]
pub text: Option<String>,
}
impl From<NewUnstablePollStartEventContent> for NewUnstablePollStartEventContentWithoutRelation {
fn from(value: NewUnstablePollStartEventContent) -> Self {
let NewUnstablePollStartEventContent { poll_start, text, .. } = value;
Self { poll_start, text }
}
}
#[derive(Clone, Debug)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct ReplacementUnstablePollStartEventContent {
pub poll_start: Option<UnstablePollStartContentBlock>,
pub text: Option<String>,
pub relates_to: Replacement<NewUnstablePollStartEventContentWithoutRelation>,
}
impl ReplacementUnstablePollStartEventContent {
pub fn new(poll_start: UnstablePollStartContentBlock, replaces: OwnedEventId) -> Self {
Self {
poll_start: None,
text: None,
relates_to: Replacement {
event_id: replaces,
new_content: NewUnstablePollStartEventContent::new(poll_start).into(),
},
}
}
pub fn plain_text(
text: impl Into<String>,
poll_start: UnstablePollStartContentBlock,
replaces: OwnedEventId,
) -> Self {
Self {
poll_start: None,
text: None,
relates_to: Replacement {
event_id: replaces,
new_content: NewUnstablePollStartEventContent::plain_text(text, poll_start).into(),
},
}
}
}
impl EventContent for ReplacementUnstablePollStartEventContent {
type EventType = MessageLikeEventType;
fn event_type(&self) -> Self::EventType {
MessageLikeEventType::UnstablePollStart
}
}
impl StaticEventContent for ReplacementUnstablePollStartEventContent {
const TYPE: &'static str = "org.matrix.msc3381.poll.start";
}
impl MessageLikeEventContent for ReplacementUnstablePollStartEventContent {}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct RedactedUnstablePollStartEventContent {}
impl RedactedUnstablePollStartEventContent {
pub fn new() -> RedactedUnstablePollStartEventContent {
Self::default()
}
}
impl EventContent for RedactedUnstablePollStartEventContent {
type EventType = MessageLikeEventType;
fn event_type(&self) -> Self::EventType {
MessageLikeEventType::UnstablePollStart
}
}
impl StaticEventContent for RedactedUnstablePollStartEventContent {
const TYPE: &'static str = "org.matrix.msc3381.poll.start";
}
impl RedactedMessageLikeEventContent for RedactedUnstablePollStartEventContent {}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct UnstablePollStartContentBlock {
pub question: UnstablePollQuestion,
#[serde(default, with = "unstable_poll_kind_serde")]
pub kind: PollKind,
#[serde(default = "PollContentBlock::default_max_selections")]
pub max_selections: UInt,
pub answers: UnstablePollAnswers,
}
impl UnstablePollStartContentBlock {
pub fn new(question: impl Into<String>, answers: UnstablePollAnswers) -> Self {
Self {
question: UnstablePollQuestion::new(question),
kind: Default::default(),
max_selections: PollContentBlock::default_max_selections(),
answers,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct UnstablePollQuestion {
#[serde(rename = "org.matrix.msc1767.text")]
pub text: String,
}
impl UnstablePollQuestion {
pub fn new(text: impl Into<String>) -> Self {
Self { text: text.into() }
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(try_from = "UnstablePollAnswersDeHelper")]
pub struct UnstablePollAnswers(Vec<UnstablePollAnswer>);
impl TryFrom<Vec<UnstablePollAnswer>> for UnstablePollAnswers {
type Error = PollAnswersError;
fn try_from(value: Vec<UnstablePollAnswer>) -> Result<Self, Self::Error> {
if value.len() < PollAnswers::MIN_LENGTH {
Err(PollAnswersError::NotEnoughValues)
} else if value.len() > PollAnswers::MAX_LENGTH {
Err(PollAnswersError::TooManyValues)
} else {
Ok(Self(value))
}
}
}
impl TryFrom<&[UnstablePollAnswer]> for UnstablePollAnswers {
type Error = PollAnswersError;
fn try_from(value: &[UnstablePollAnswer]) -> Result<Self, Self::Error> {
Self::try_from(value.to_owned())
}
}
impl Deref for UnstablePollAnswers {
type Target = [UnstablePollAnswer];
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct UnstablePollAnswer {
pub id: String,
#[serde(rename = "org.matrix.msc1767.text")]
pub text: String,
}
impl UnstablePollAnswer {
pub fn new(id: impl Into<String>, text: impl Into<String>) -> Self {
Self { id: id.into(), text: text.into() }
}
}