diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/channel/app.rs | 16 | ||||
| -rw-r--r-- | src/channel/routes.rs | 2 | ||||
| -rw-r--r-- | src/db/mod.rs | 30 | ||||
| -rw-r--r-- | src/invite/app.rs | 10 | ||||
| -rw-r--r-- | src/invite/routes.rs | 3 | ||||
| -rw-r--r-- | src/setup/middleware.rs | 6 |
6 files changed, 49 insertions, 18 deletions
diff --git a/src/channel/app.rs b/src/channel/app.rs index 7c0b107..5d6cada 100644 --- a/src/channel/app.rs +++ b/src/channel/app.rs @@ -5,7 +5,7 @@ use sqlx::sqlite::SqlitePool; use super::{repo::Provider as _, Channel, History, Id}; use crate::{ clock::DateTime, - db::NotFound, + db::{Duplicate as _, NotFound as _}, event::{repo::Provider as _, Broadcaster, Event, Sequence}, message::repo::Provider as _, }; @@ -27,7 +27,7 @@ impl<'a> Channels<'a> { .channels() .create(name, &created) .await - .map_err(|err| CreateError::from_duplicate_name(err, name))?; + .duplicate(|| CreateError::DuplicateName(name.into()))?; tx.commit().await?; self.events @@ -133,18 +133,6 @@ pub enum Error { DatabaseError(#[from] sqlx::Error), } -impl CreateError { - fn from_duplicate_name(error: sqlx::Error, name: &str) -> Self { - if let Some(error) = error.as_database_error() { - if error.is_unique_violation() { - return Self::DuplicateName(name.into()); - } - } - - Self::from(error) - } -} - #[derive(Debug, thiserror::Error)] pub enum InternalError { #[error(transparent)] diff --git a/src/channel/routes.rs b/src/channel/routes.rs index e97c447..eaf7962 100644 --- a/src/channel/routes.rs +++ b/src/channel/routes.rs @@ -53,7 +53,7 @@ impl IntoResponse for CreateError { let Self(error) = self; match error { duplicate @ app::CreateError::DuplicateName(_) => { - (StatusCode::BAD_REQUEST, duplicate.to_string()).into_response() + (StatusCode::CONFLICT, duplicate.to_string()).into_response() } other => Internal::from(other).into_response(), } diff --git a/src/db/mod.rs b/src/db/mod.rs index fa3d74e..6005813 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -4,6 +4,7 @@ use std::str::FromStr; use hex_literal::hex; use sqlx::{ + error::{DatabaseError, ErrorKind}, migrate::{Migrate as _, MigrateDatabase as _}, sqlite::{Sqlite, SqliteConnectOptions, SqlitePool, SqlitePoolOptions}, }; @@ -161,3 +162,32 @@ impl<T> NotFound for Result<T, sqlx::Error> { self.optional()?.ok_or_else(map) } } + +pub trait Duplicate { + type Ok; + type Error; + + fn duplicate<E, F>(self, map: F) -> Result<Self::Ok, E> + where + E: From<Self::Error>, + F: FnOnce() -> E; +} + +impl<T> Duplicate for Result<T, sqlx::Error> { + type Ok = T; + type Error = sqlx::Error; + + fn duplicate<E, F>(self, map: F) -> Result<T, E> + where + E: From<sqlx::Error>, + F: FnOnce() -> E, + { + match self { + Ok(value) => Ok(value), + Err(error) => match error.as_database_error().map(DatabaseError::kind) { + Some(ErrorKind::UniqueViolation) => Err(map()), + _ => Err(error.into()), + }, + } + } +} diff --git a/src/invite/app.rs b/src/invite/app.rs index 998b4f1..6800d72 100644 --- a/src/invite/app.rs +++ b/src/invite/app.rs @@ -4,7 +4,7 @@ use sqlx::sqlite::SqlitePool; use super::{repo::Provider as _, Id, Invite, Summary}; use crate::{ clock::DateTime, - db::NotFound as _, + db::{Duplicate as _, NotFound as _}, event::repo::Provider as _, login::{repo::Provider as _, Login, Password}, token::{repo::Provider as _, Secret}, @@ -68,7 +68,11 @@ impl<'a> Invites<'a> { // catch it. tx.invites().accept(&invite).await?; let created = tx.sequence().next(accepted_at).await?; - let login = tx.logins().create(name, &password_hash, &created).await?; + let login = tx + .logins() + .create(name, &password_hash, &created) + .await + .duplicate(|| AcceptError::DuplicateLogin(name.into()))?; let secret = tx.tokens().issue(&login, accepted_at).await?; tx.commit().await?; @@ -99,6 +103,8 @@ pub enum Error { pub enum AcceptError { #[error("invite not found: {0}")] NotFound(Id), + #[error("name in use: {0}")] + DuplicateLogin(String), #[error(transparent)] Database(#[from] sqlx::Error), #[error(transparent)] diff --git a/src/invite/routes.rs b/src/invite/routes.rs index 3384e10..977fe9b 100644 --- a/src/invite/routes.rs +++ b/src/invite/routes.rs @@ -88,6 +88,9 @@ impl IntoResponse for AcceptError { let Self(error) = self; match error { error @ app::AcceptError::NotFound(_) => NotFound(error).into_response(), + error @ app::AcceptError::DuplicateLogin(_) => { + (StatusCode::CONFLICT, error.to_string()).into_response() + } other => Internal::from(other).into_response(), } } diff --git a/src/setup/middleware.rs b/src/setup/middleware.rs index a5f9070..5f9996b 100644 --- a/src/setup/middleware.rs +++ b/src/setup/middleware.rs @@ -10,7 +10,11 @@ use crate::{app::App, error::Internal}; pub async fn setup_required(State(app): State<App>, request: Request, next: Next) -> Response { match app.setup().completed().await { Ok(true) => next.run(request).await, - Ok(false) => (StatusCode::CONFLICT, "initial setup not completed").into_response(), + Ok(false) => ( + StatusCode::SERVICE_UNAVAILABLE, + "initial setup not completed", + ) + .into_response(), Err(error) => Internal::from(error).into_response(), } } |
