diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2025-03-23 15:58:33 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2025-03-23 16:25:22 -0400 |
| commit | 2420f1e75d54a5f209b0267715f078a369d81eb1 (patch) | |
| tree | 20edd531a3f2f765a23fef8e7a508c91bc7dc294 /src/user/create.rs | |
| parent | 7e15690d54ff849596401b43d163df9353062850 (diff) | |
Rename the `login` module to `user`.
Diffstat (limited to 'src/user/create.rs')
| -rw-r--r-- | src/user/create.rs | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/src/user/create.rs b/src/user/create.rs new file mode 100644 index 0000000..da94685 --- /dev/null +++ b/src/user/create.rs @@ -0,0 +1,95 @@ +use sqlx::{Transaction, sqlite::Sqlite}; + +use super::{History, Password, password::StoredHash, repo::Provider as _, validate}; +use crate::{ + clock::DateTime, + event::{Broadcaster, Event, repo::Provider as _}, + name::Name, +}; + +#[must_use = "dropping a user creation attempt is likely a mistake"] +pub struct Create<'a> { + name: &'a Name, + password: &'a Password, + created_at: &'a DateTime, +} + +impl<'a> Create<'a> { + pub fn begin(name: &'a Name, password: &'a Password, created_at: &'a DateTime) -> Self { + Self { + name, + password, + created_at, + } + } + + pub fn validate(self) -> Result<Validated<'a>, 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, + }) + } +} + +#[must_use = "dropping a user creation attempt is likely a mistake"] +pub struct Validated<'a> { + name: &'a Name, + password_hash: StoredHash, + created_at: &'a DateTime, +} + +impl Validated<'_> { + pub async fn store(self, tx: &mut Transaction<'_, Sqlite>) -> Result<Stored, sqlx::Error> { + let Self { + name, + password_hash, + created_at, + } = self; + + let created = tx.sequence().next(created_at).await?; + let user = tx.users().create(name, &password_hash, &created).await?; + + Ok(Stored { user }) + } +} + +#[must_use = "dropping a user creation attempt is likely a mistake"] +pub struct Stored { + user: History, +} + +impl Stored { + #[must_use = "dropping a user creation attempt is likely a mistake"] + pub fn publish(self, events: &Broadcaster) -> History { + let Self { user } = self; + + events.broadcast(user.events().map(Event::from).collect::<Vec<_>>()); + + user + } + + pub fn user(&self) -> &History { + &self.user + } +} + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("invalid user name: {0}")] + InvalidName(Name), + #[error(transparent)] + PasswordHash(#[from] password_hash::Error), +} |
