use sqlx::{sqlite::Sqlite, SqliteConnection, Transaction}; use crate::{id::Id as BaseId, password::StoredHash}; pub trait Provider { fn logins(&mut self) -> Logins; } impl<'c> Provider for Transaction<'c, Sqlite> { fn logins(&mut self) -> Logins { Logins(self) } } pub struct Logins<'t>(&'t mut SqliteConnection); // This also implements FromRequestParts (see `./extract.rs`). As a result, it // can be used as an extractor for endpoints that want to require login, or for // endpoints that need to behave differently depending on whether the client is // or is not logged in. #[derive(Clone, Debug, serde::Serialize)] pub struct Login { pub id: Id, pub name: String, // The omission of the hashed password is deliberate, to minimize the // chance that it ends up tangled up in debug output or in some other chunk // of logic elsewhere. } impl<'c> Logins<'c> { pub async fn create( &mut self, name: &str, password_hash: &StoredHash, ) -> Result { let id = Id::generate(); let login = sqlx::query_as!( Login, r#" insert or fail into login (id, name, password_hash) values ($1, $2, $3) returning id as "id: Id", name "#, id, name, password_hash, ) .fetch_one(&mut *self.0) .await?; Ok(login) } pub async fn by_id(&mut self, id: &Id) -> Result { let login = sqlx::query_as!( Login, r#" select id as "id: Id", name from login where id = $1 "#, id, ) .fetch_one(&mut *self.0) .await?; Ok(login) } } impl<'t> From<&'t mut SqliteConnection> for Logins<'t> { fn from(tx: &'t mut SqliteConnection) -> Self { Self(tx) } } /// Stable identifier for a [Login]. Prefixed with `L`. #[derive(Clone, Debug, Eq, PartialEq, sqlx::Type, serde::Serialize)] #[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("L") } } impl std::fmt::Display for Id { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } }