summaryrefslogtreecommitdiff
path: root/src/login/repo.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/login/repo.rs')
-rw-r--r--src/login/repo.rs145
1 files changed, 145 insertions, 0 deletions
diff --git a/src/login/repo.rs b/src/login/repo.rs
new file mode 100644
index 0000000..5be91ad
--- /dev/null
+++ b/src/login/repo.rs
@@ -0,0 +1,145 @@
+use sqlx::{SqliteConnection, Transaction, sqlite::Sqlite};
+
+use super::{Id, Login};
+use crate::{
+ db::NotFound,
+ name::{self, Name},
+ password::StoredHash,
+};
+
+pub trait Provider {
+ fn logins(&mut self) -> Logins<'_>;
+}
+
+impl Provider for Transaction<'_, Sqlite> {
+ fn logins(&mut self) -> Logins<'_> {
+ Logins(self)
+ }
+}
+
+pub struct Logins<'t>(&'t mut SqliteConnection);
+
+impl Logins<'_> {
+ pub async fn create(
+ &mut self,
+ login: &Login,
+ password: &StoredHash,
+ ) -> Result<(), sqlx::Error> {
+ let Login { id, name } = login;
+ let display_name = name.display();
+ let canonical_name = name.canonical();
+
+ sqlx::query!(
+ r#"
+ insert into login (id, display_name, canonical_name, password)
+ values ($1, $2, $3, $4)
+ "#,
+ id,
+ display_name,
+ canonical_name,
+ password,
+ )
+ .execute(&mut *self.0)
+ .await?;
+
+ Ok(())
+ }
+
+ pub async fn by_id(&mut self, id: &Id) -> Result<(Login, StoredHash), LoadError> {
+ let user = sqlx::query!(
+ r#"
+ select
+ id as "id: Id",
+ display_name,
+ canonical_name,
+ password as "password: StoredHash"
+ from login
+ where id = $1
+ "#,
+ id,
+ )
+ .map(|row| {
+ Ok::<_, LoadError>((
+ Login {
+ id: row.id,
+ name: Name::new(row.display_name, row.canonical_name)?,
+ },
+ row.password,
+ ))
+ })
+ .fetch_one(&mut *self.0)
+ .await??;
+
+ Ok(user)
+ }
+
+ pub async fn by_name(&mut self, name: &Name) -> Result<(Login, StoredHash), LoadError> {
+ let canonical_name = name.canonical();
+
+ let (login, password) = sqlx::query!(
+ r#"
+ select
+ id as "id: Id",
+ display_name,
+ canonical_name,
+ password as "password: StoredHash"
+ from login
+ where canonical_name = $1
+ "#,
+ canonical_name,
+ )
+ .map(|row| {
+ Ok::<_, LoadError>((
+ Login {
+ id: row.id,
+ name: Name::new(row.display_name, row.canonical_name)?,
+ },
+ row.password,
+ ))
+ })
+ .fetch_one(&mut *self.0)
+ .await??;
+
+ Ok((login, password))
+ }
+
+ pub async fn set_password(
+ &mut self,
+ login: &Login,
+ password: &StoredHash,
+ ) -> Result<(), sqlx::Error> {
+ sqlx::query!(
+ r#"
+ update login
+ set password = $1
+ where id = $2
+ "#,
+ password,
+ login.id,
+ )
+ .execute(&mut *self.0)
+ .await?;
+
+ Ok(())
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error(transparent)]
+pub enum LoadError {
+ Database(#[from] sqlx::Error),
+ Name(#[from] name::Error),
+}
+
+impl<T> NotFound for Result<T, LoadError> {
+ type Ok = T;
+ type Error = LoadError;
+
+ fn optional(self) -> Result<Option<T>, LoadError> {
+ match self {
+ Ok(value) => Ok(Some(value)),
+ Err(LoadError::Database(sqlx::Error::RowNotFound)) => Ok(None),
+ Err(other) => Err(other),
+ }
+ }
+}