use sqlx::{sqlite::Sqlite, Transaction}; use super::{password::StoredHash, repo::Provider as _, validate, History, Password}; use crate::{ clock::DateTime, event::{repo::Provider as _, Broadcaster, Event}, name::Name, }; pub struct Create<'a> { name: &'a Name, password: &'a Password, created_at: &'a DateTime, } impl<'a> Create<'a> { #[must_use = "dropping a login creation attempt is likely a mistake"] pub fn begin(name: &'a Name, password: &'a Password, created_at: &'a DateTime) -> Self { Self { name, password, created_at, } } #[must_use = "dropping a login creation attempt is likely a mistake"] pub fn validate(self) -> Result, Error> { let Self { name, password, created_at, } = self; if !validate::name(name) { return Err(Error::InvalidName(name.clone())); } let password_hash = password.hash()?; Ok(Validated { name, password_hash, created_at, }) } } pub struct Validated<'a> { name: &'a Name, password_hash: StoredHash, created_at: &'a DateTime, } impl<'a> Validated<'a> { #[must_use = "dropping a login creation attempt is likely a mistake"] pub async fn store<'c>(self, tx: &mut Transaction<'c, Sqlite>) -> Result { let Self { name, password_hash, created_at, } = self; let created = tx.sequence().next(created_at).await?; let login = tx.logins().create(name, &password_hash, &created).await?; Ok(Stored { login }) } } pub struct Stored { login: History, } impl Stored { #[must_use = "dropping a login creation attempt is likely a mistake"] pub fn publish(self, events: &Broadcaster) -> History { let Self { login } = self; events.broadcast(login.events().map(Event::from).collect::>()); login } pub fn login(&self) -> &History { &self.login } } #[derive(Debug, thiserror::Error)] pub enum Error { #[error("invalid login name: {0}")] InvalidName(Name), #[error(transparent)] PasswordHash(#[from] password_hash::Error), }