diff options
Diffstat (limited to 'src/login/repo.rs')
| -rw-r--r-- | src/login/repo.rs | 145 |
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), + } + } +} |
