use std::fmt; use sqlx::{sqlite::Sqlite, SqliteConnection, Transaction}; use crate::id::Id as BaseId; use crate::{error::BoxedError, login::repo::logins::Id as LoginId}; 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(Debug)] pub struct Channel { pub id: Id, pub name: String, } impl<'c> Channels<'c> { /// Create a new channel. pub async fn create(&mut self, name: &str) -> Result { let id = Id::generate(); let channel = sqlx::query_as!( Channel, r#" insert into channel (id, name) values ($1, $2) returning id as "id: Id", name "#, id, name, ) .fetch_one(&mut *self.0) .await?; Ok(channel) } /// Enrol a login in a channel. pub async fn join(&mut self, channel: &Id, login: &LoginId) -> Result<(), BoxedError> { sqlx::query!( r#" insert into channel_member (channel, login) values ($1, $2) "#, channel, login, ) .execute(&mut *self.0) .await?; Ok(()) } pub async fn for_login(&mut self, login: &LoginId) -> Result, BoxedError> { let channels = sqlx::query_as!( Channel, r#" select channel.id as "id: Id", channel.name from channel join channel_member on (channel.id = channel_member.channel) where channel_member.login = $1 order by channel.name "#, login, ) .fetch_all(&mut *self.0) .await?; Ok(channels) } } /// Stable identifier for a [Channel]. Prefixed with `C`. #[derive(Debug, sqlx::Type)] #[sqlx(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) } }