use sqlx::{SqliteConnection, Transaction, sqlite::Sqlite}; use crate::{ clock::DateTime, db::NotFound, event::{Instant, Sequence}, name::{self, Name}, password::StoredHash, user::{self, History, User}, }; pub trait Provider { fn auth(&mut self) -> Auth<'_>; } impl Provider for Transaction<'_, Sqlite> { fn auth(&mut self) -> Auth<'_> { Auth(self) } } pub struct Auth<'t>(&'t mut SqliteConnection); impl Auth<'_> { pub async fn for_name(&mut self, name: &Name) -> Result<(History, StoredHash), LoadError> { let name = name.canonical(); let row = sqlx::query!( r#" select id as "id: user::Id", display_name as "display_name: String", canonical_name as "canonical_name: String", created_sequence as "created_sequence: Sequence", created_at as "created_at: DateTime", password_hash as "password_hash: StoredHash" from user where canonical_name = $1 "#, name, ) .fetch_one(&mut *self.0) .await?; let login = History { user: User { id: row.id, name: Name::new(row.display_name, row.canonical_name)?, }, created: Instant::new(row.created_at, row.created_sequence), }; Ok((login, row.password_hash)) } pub async fn for_user(&mut self, user: &User) -> Result<(History, StoredHash), LoadError> { let row = sqlx::query!( r#" select id as "id: user::Id", display_name as "display_name: String", canonical_name as "canonical_name: String", created_sequence as "created_sequence: Sequence", created_at as "created_at: DateTime", password_hash as "password_hash: StoredHash" from user where id = $1 "#, user.id, ) .fetch_one(&mut *self.0) .await?; let user = History { user: User { id: row.id, name: Name::new(row.display_name, row.canonical_name)?, }, created: Instant::new(row.created_at, row.created_sequence), }; Ok((user, row.password_hash)) } } #[derive(Debug, thiserror::Error)] #[error(transparent)] pub enum LoadError { Database(#[from] sqlx::Error), Name(#[from] name::Error), } impl NotFound for Result { type Ok = T; type Error = LoadError; fn optional(self) -> Result, LoadError> { match self { Ok(value) => Ok(Some(value)), Err(LoadError::Database(sqlx::Error::RowNotFound)) => Ok(None), Err(other) => Err(other), } } }