use std::fmt; use sqlx::{sqlite::Sqlite, SqliteConnection, Transaction}; use crate::{ clock::DateTime, events::types::{self, Sequence}, id::Id as BaseId, }; pub trait Provider { fn channels(&mut self) -> Channels; } impl<'c> Provider for Transaction<'c, Sqlite> { fn channels(&mut self) -> Channels { Channels(self) } } pub struct Channels<'t>(&'t mut SqliteConnection); #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] pub struct Channel { pub id: Id, pub name: String, #[serde(skip)] pub created_at: DateTime, } impl<'c> Channels<'c> { pub async fn create( &mut self, name: &str, created_at: &DateTime, ) -> Result { let id = Id::generate(); let sequence = Sequence::default(); let channel = sqlx::query_as!( Channel, r#" insert into channel (id, name, created_at, last_sequence) values ($1, $2, $3, $4) returning id as "id: Id", name, created_at as "created_at: DateTime" "#, id, name, created_at, sequence, ) .fetch_one(&mut *self.0) .await?; Ok(channel) } pub async fn by_id(&mut self, channel: &Id) -> Result { let channel = sqlx::query_as!( Channel, r#" select id as "id: Id", name, created_at as "created_at: DateTime" from channel where id = $1 "#, channel, ) .fetch_one(&mut *self.0) .await?; Ok(channel) } pub async fn all(&mut self) -> Result, sqlx::Error> { let channels = sqlx::query_as!( Channel, r#" select id as "id: Id", name, created_at as "created_at: DateTime" from channel order by channel.name "#, ) .fetch_all(&mut *self.0) .await?; Ok(channels) } pub async fn delete_expired( &mut self, channel: &Channel, sequence: Sequence, deleted_at: &DateTime, ) -> Result { let channel = channel.id.clone(); sqlx::query_scalar!( r#" delete from channel where id = $1 returning 1 as "row: i64" "#, channel, ) .fetch_one(&mut *self.0) .await?; Ok(types::ChannelEvent { sequence, at: *deleted_at, data: types::DeletedEvent { channel }.into(), }) } pub async fn expired(&mut self, expired_at: &DateTime) -> Result, sqlx::Error> { let channels = sqlx::query_as!( Channel, r#" select channel.id as "id: Id", channel.name, channel.created_at as "created_at: DateTime" from channel left join message where created_at < $1 and message.id is null "#, expired_at, ) .fetch_all(&mut *self.0) .await?; Ok(channels) } } // Stable identifier for a [Channel]. Prefixed with `C`. #[derive( Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, 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("C") } } impl fmt::Display for Id { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } }