diff --git a/data/src/message.rs b/data/src/message.rs index 9cc3461bf..a22736927 100644 --- a/data/src/message.rs +++ b/data/src/message.rs @@ -1,6 +1,9 @@ +use std::borrow::Cow; + use chrono::{DateTime, Utc}; use irc::proto; use irc::proto::Command; +use itertools::Itertools; use once_cell::sync::Lazy; use regex::Regex; use serde::{Deserialize, Serialize}; @@ -86,7 +89,7 @@ pub enum Direction { Received, } -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone)] pub struct Message { pub received_at: Posix, pub server_time: DateTime, @@ -158,6 +161,35 @@ impl Message { } } +impl Serialize for Message { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + #[derive(Serialize)] + struct Data<'a> { + received_at: &'a Posix, + server_time: &'a DateTime, + direction: &'a Direction, + target: &'a Target, + content: &'a Content, + // Old field before we had fragments, + // added for downgrade compatability + text: Cow<'a, str>, + } + + Data { + received_at: &self.received_at, + server_time: &self.server_time, + direction: &self.direction, + target: &self.target, + content: &self.content, + text: self.content.text(), + } + .serialize(serializer) + } +} + impl<'de> Deserialize<'de> for Message { fn deserialize(deserializer: D) -> Result where @@ -169,7 +201,7 @@ impl<'de> Deserialize<'de> for Message { server_time: DateTime, direction: Direction, target: Target, - // New field + // New field, optional for upgrade compatability content: Option, // Old field before we had fragments text: Option, @@ -184,10 +216,11 @@ impl<'de> Deserialize<'de> for Message { text, } = Data::deserialize(deserializer)?; - let content = if let Some(text) = text { - parse_fragments(text) - } else if let Some(content) = content { + let content = if let Some(content) = content { content + } else if let Some(text) = text { + // First time upgrading, convert text into content + parse_fragments(text) } else { // Unreachable Content::Plain("".to_string()) @@ -239,6 +272,15 @@ pub enum Content { Fragments(Vec), } +impl Content { + fn text(&self) -> Cow { + match self { + Content::Plain(s) => s.into(), + Content::Fragments(fragments) => fragments.iter().map(Fragment::as_str).join("").into(), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum Fragment { Text(String),