use sqlx::{sqlite::Sqlite, SqliteConnection, Transaction}; use crate::{ clock::DateTime, db::NotFound, event::{Instant, Sequence}, login::{self, password::StoredHash, History, Login}, name::{self, Name}, }; pub trait Provider { fn auth(&mut self) -> Auth; } impl<'c> Provider for Transaction<'c, Sqlite> { fn auth(&mut self) -> Auth { Auth(self) } } pub struct Auth<'t>(&'t mut SqliteConnection); impl<'t> Auth<'t> { 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: login::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 login where canonical_name = $1 "#, name, ) .fetch_one(&mut *self.0) .await?; let login = History { login: Login { 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)) } } #[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), } } }