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, }; 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)] pub struct Message { pub id: Id, pub sender: LoginId, // Field not actually used at this time, but you can reinstate it if you // need to. It's not omitted out of any greater design intention. // pub channel: ChannelId, 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 message = sqlx::query_as!( Message, r#" insert into message (id, sender, channel, body, sent_at) values ($1, $2, $3, $4, $5) returning id as "id: Id", sender as "sender: LoginId", -- channel as "channel: ChannelId", body, sent_at as "sent_at: DateTime" "#, id, sender, channel, body, sent_at, ) .fetch_one(&mut *self.0) .await?; Ok(message) } pub async fn all(&mut self, channel: &ChannelId) -> Result, BoxedError> { let messages = sqlx::query_as!( Message, r#" select id as "id: Id", sender as "sender: LoginId", -- channel as "channel: ChannelId", body, sent_at as "sent_at: DateTime" from message where channel = $1 order by sent_at asc "#, channel, ) .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) } }