summaryrefslogtreecommitdiff
path: root/src/user/create.rs
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2025-03-23 15:58:33 -0400
committerOwen Jacobson <owen@grimoire.ca>2025-03-23 16:25:22 -0400
commit2420f1e75d54a5f209b0267715f078a369d81eb1 (patch)
tree20edd531a3f2f765a23fef8e7a508c91bc7dc294 /src/user/create.rs
parent7e15690d54ff849596401b43d163df9353062850 (diff)
Rename the `login` module to `user`.
Diffstat (limited to 'src/user/create.rs')
-rw-r--r--src/user/create.rs95
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),
+}