summaryrefslogtreecommitdiff
path: root/src/message/snapshot.rs
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2024-10-02 12:25:36 -0400
committerOwen Jacobson <owen@grimoire.ca>2024-10-03 19:25:41 -0400
commitec804134c33aedb001c426c5f42f43f53c47848f (patch)
treec62b59ab5cdd438f47a5f9cc35fdc712d362af19 /src/message/snapshot.rs
parent469613872f6fb19f4579b387e19b2bc38fa52f51 (diff)
Represent channels and messages using a split "History" and "Snapshot" model.
This separates the code that figures out what happened to an entity from the code that represents it to a user, and makes it easier to compute a snapshot at a point in time (for things like bootstrap). It also makes the internal logic a bit easier to follow, since it's easier to tell whether you're working with a point in time or with the whole recorded history. This hefty.
Diffstat (limited to 'src/message/snapshot.rs')
-rw-r--r--src/message/snapshot.rs74
1 files changed, 74 insertions, 0 deletions
diff --git a/src/message/snapshot.rs b/src/message/snapshot.rs
new file mode 100644
index 0000000..3adccbe
--- /dev/null
+++ b/src/message/snapshot.rs
@@ -0,0 +1,74 @@
+use super::{
+ event::{Event, Kind, Sent},
+ Id,
+};
+use crate::{channel::Channel, login::Login};
+
+#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
+#[serde(into = "self::serialize::Message")]
+pub struct Message {
+ pub channel: Channel,
+ pub sender: Login,
+ pub id: Id,
+ pub body: String,
+}
+
+mod serialize {
+ use crate::{channel::Channel, login::Login, message::Id};
+
+ #[derive(serde::Serialize)]
+ pub struct Message {
+ channel: Channel,
+ sender: Login,
+ #[allow(clippy::struct_field_names)]
+ // Deliberately redundant with the module path; this produces a specific serialization.
+ message: MessageData,
+ }
+
+ #[derive(serde::Serialize)]
+ pub struct MessageData {
+ id: Id,
+ body: String,
+ }
+
+ impl From<super::Message> for Message {
+ fn from(message: super::Message) -> Self {
+ Self {
+ channel: message.channel,
+ sender: message.sender,
+ message: MessageData {
+ id: message.id,
+ body: message.body,
+ },
+ }
+ }
+ }
+}
+
+impl Message {
+ fn apply(state: Option<Self>, event: Event) -> Option<Self> {
+ match (state, event.kind) {
+ (None, Kind::Sent(event)) => Some(event.into()),
+ (Some(message), Kind::Deleted(event)) if message.id == event.message => None,
+ (state, event) => panic!("invalid message event {event:#?} for state {state:#?}"),
+ }
+ }
+}
+
+impl FromIterator<Event> for Option<Message> {
+ fn from_iter<I: IntoIterator<Item = Event>>(events: I) -> Self {
+ events.into_iter().fold(None, Message::apply)
+ }
+}
+
+impl From<&Sent> for Message {
+ fn from(event: &Sent) -> Self {
+ event.message.clone()
+ }
+}
+
+impl From<Sent> for Message {
+ fn from(event: Sent) -> Self {
+ event.message
+ }
+}