use std::fmt; use sqlx::{sqlite::Sqlite, SqliteConnection, Transaction}; use super::channels::Id as ChannelId; use crate::{ clock::DateTime, error::BoxedError, id::Id as BaseId, login::repo::logins::{Id as LoginId, Login, Logins}, }; pub trait Provider { fn messages(&mut self) -> Messages; } impl<'c> Provider for Transaction<'c, Sqlite> { fn messages(&mut self) -> Messages { Messages(self) } } pub struct Messages<'t>(&'t mut SqliteConnection); #[derive(Clone, Debug, serde::Serialize)] pub struct BroadcastMessage { pub id: Id, pub sender: Login, pub body: String, pub sent_at: DateTime, } impl<'c> Messages<'c> { pub async fn create( &mut self, sender: &LoginId, channel: &ChannelId, body: &str, sent_at: &DateTime, ) -> Result { let id = Id::generate(); let sender = Logins::from(&mut *self.0).by_id(sender).await?; let message = sqlx::query!( r#" insert into message (id, sender, channel, body, sent_at) values ($1, $2, $3, $4, $5) returning id as "id: Id", body, sent_at as "sent_at: DateTime" "#, id, sender.id, channel, body, sent_at, ) .map(|row| BroadcastMessage { sender: sender.clone(), id: row.id, body: row.body, sent_at: row.sent_at, }) .fetch_one(&mut *self.0) .await?; Ok(message) } pub async fn for_replay( &mut self, channel: &ChannelId, ) -> Result, BoxedError> { let messages = sqlx::query!( r#" select message.id as "id: Id", login.id as "sender_id: LoginId", login.name as sender_name, message.body, message.sent_at as "sent_at: DateTime" from message join login on message.sender = login.id where channel = $1 order by sent_at asc "#, channel, ) .map(|row| BroadcastMessage { id: row.id, sender: Login { id: row.sender_id, name: row.sender_name, }, body: row.body, sent_at: row.sent_at, }) .fetch_all(&mut *self.0) .await?; Ok(messages) } } /// Stable identifier for a [Message]. Prefixed with `M`. #[derive(Clone, Debug, Eq, Hash, PartialEq, sqlx::Type, serde::Deserialize, serde::Serialize)] #[sqlx(transparent)] #[serde(transparent)] pub struct Id(BaseId); impl From for Id { fn from(id: BaseId) -> Self { Self(id) } } impl Id { pub fn generate() -> Self { BaseId::generate("M") } } impl fmt::Display for Id { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } }