use serde::{Deserialize, Serialize};
use crate::domain::core::Message;
#[derive(
Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
)]
#[serde(transparent)]
pub struct Seq(u64);
impl Seq {
pub const START: Seq = Seq(0);
pub fn new(value: u64) -> Self {
Self(value)
}
pub fn get(self) -> u64 {
self.0
}
}
impl std::fmt::Display for Seq {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone)]
pub struct SequencedMessage {
pub seq: Seq,
pub message: Message,
}
#[derive(Debug, Clone)]
pub struct Digest {
pub covers_through: Seq,
pub summary: String,
pub replaced_messages: u32,
pub model: String,
}
#[derive(Debug, Clone, Default)]
pub struct Conversation {
pub digest: Option<Digest>,
pub tail: Vec<SequencedMessage>,
}
impl Conversation {
pub fn is_empty(&self) -> bool {
self.digest.is_none() && self.tail.is_empty()
}
pub fn summary(&self) -> Option<&str> {
self.digest.as_ref().map(|digest| digest.summary.as_str())
}
pub fn watermark(&self) -> Seq {
self.tail
.last()
.map(|message| message.seq)
.or_else(|| self.digest.as_ref().map(|digest| digest.covers_through))
.unwrap_or(Seq::START)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn sequenced(seq: u64) -> SequencedMessage {
SequencedMessage {
seq: Seq::new(seq),
message: Message::default(),
}
}
#[test]
fn an_empty_conversation_watermarks_at_the_start() {
assert_eq!(Conversation::default().watermark(), Seq::START);
assert!(Conversation::default().is_empty());
}
#[test]
fn the_watermark_follows_the_newest_message() {
let conversation = Conversation {
digest: None,
tail: vec![sequenced(4), sequenced(9)],
};
assert_eq!(conversation.watermark(), Seq::new(9));
}
#[test]
fn a_fully_compacted_conversation_keeps_the_digest_watermark() {
let conversation = Conversation {
digest: Some(Digest {
covers_through: Seq::new(12),
summary: "they talked".to_string(),
replaced_messages: 6,
model: "test".to_string(),
}),
tail: Vec::new(),
};
assert_eq!(conversation.watermark(), Seq::new(12));
assert!(!conversation.is_empty());
}
}