use itertools::Itertools as _; use super::{ Body, Id, Message, event::{Deleted, Event, Sent}, }; use crate::{ conversation::Conversation, event::{Instant, Sequence}, user::{self, User}, }; #[derive(Clone, Debug, Eq, PartialEq)] pub struct History { pub message: Message, } // Lifecycle interface impl History { pub fn begin(conversation: &Conversation, sender: &User, body: &Body, sent: Instant) -> Self { Self { message: Message { id: Id::generate(), conversation: conversation.id.clone(), sender: sender.id.clone(), body: body.clone(), sent, deleted: None, }, } } pub fn delete(self, deleted: Instant) -> Result { if self.message.deleted.is_none() { Ok(Self { message: Message { deleted: Some(deleted), ..self.message }, }) } else { Err(DeleteError::Deleted(self.into())) } } } #[derive(Debug, thiserror::Error)] pub enum DeleteError { #[error("message {} already deleted", .0.message.id)] // Payload is boxed here to avoid copying an entire `History` around in any errors this error // gets chained into. See . Deleted(Box), } // State interface impl History { pub fn id(&self) -> &Id { &self.message.id } pub fn sender(&self) -> &user::Id { &self.message.sender } // Snapshot of this message as it was when sent. (Note to the future: it's okay // if this returns a redacted or modified version of the message. If we // implement message editing by redacting the original body, then this should // return the edited message, not the original, even if that's not how it was // "as sent.") pub fn as_sent(&self) -> Message { self.message.clone() } pub fn as_of(&self, sequence: S) -> Option where S: Into, { self.events() .filter(Sequence::up_to(sequence.into())) .collect() } } // Events interface impl History { pub fn events(&self) -> impl Iterator + Clone + use<> { [self.sent()] .into_iter() .merge_by(self.deleted(), Sequence::merge) } fn sent(&self) -> Event { Sent { message: self.message.clone(), } .into() } fn deleted(&self) -> Option { self.message.deleted.map(|instant| { Deleted { instant, id: self.message.id.clone(), } .into() }) } }