use sqlx::sqlite::SqlitePool; use super::repo::Provider as _; use crate::{ clock::DateTime, event::{repo::Provider as _, Broadcaster, Event}, login::{repo::Provider as _, Login, Password}, token::{repo::Provider as _, Secret}, }; pub struct Setup<'a> { db: &'a SqlitePool, events: &'a Broadcaster, } impl<'a> Setup<'a> { pub const fn new(db: &'a SqlitePool, events: &'a Broadcaster) -> Self { Self { db, events } } pub async fn initial( &self, name: &str, password: &Password, created_at: &DateTime, ) -> Result<(Login, Secret), Error> { let password_hash = password.hash()?; let mut tx = self.db.begin().await?; let login = if tx.setup().completed().await? { Err(Error::SetupCompleted)? } else { let created = tx.sequence().next(created_at).await?; tx.logins().create(name, &password_hash, &created).await? }; let secret = tx.tokens().issue(&login, created_at).await?; tx.commit().await?; self.events .broadcast(login.events().map(Event::from).collect::>()); Ok((login.as_created(), secret)) } pub async fn completed(&self) -> Result { let mut tx = self.db.begin().await?; let completed = tx.setup().completed().await?; tx.commit().await?; Ok(completed) } } #[derive(Debug, thiserror::Error)] pub enum Error { #[error("initial setup previously completed")] SetupCompleted, #[error(transparent)] Database(#[from] sqlx::Error), #[error(transparent)] PasswordHash(#[from] password_hash::Error), }