minor cleanup with traits
This commit is contained in:
parent
73f9c95e37
commit
8a37a7b2da
17 changed files with 81 additions and 71 deletions
|
|
@ -6,7 +6,7 @@ use chattyness_db::{
|
||||||
models::{CreateServerAvatarRequest, ServerAvatar, ServerAvatarSummary, UpdateServerAvatarRequest},
|
models::{CreateServerAvatarRequest, ServerAvatar, ServerAvatarSummary, UpdateServerAvatarRequest},
|
||||||
queries::server_avatars,
|
queries::server_avatars,
|
||||||
};
|
};
|
||||||
use chattyness_error::AppError;
|
use chattyness_error::{AppError, OptionExt};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
@ -87,7 +87,7 @@ pub async fn get_avatar(
|
||||||
) -> Result<Json<ServerAvatar>, AppError> {
|
) -> Result<Json<ServerAvatar>, AppError> {
|
||||||
let avatar = server_avatars::get_server_avatar_by_id(&pool, avatar_id)
|
let avatar = server_avatars::get_server_avatar_by_id(&pool, avatar_id)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Avatar not found".to_string()))?;
|
.or_not_found("Avatar")?;
|
||||||
Ok(Json(avatar))
|
Ok(Json(avatar))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,7 +106,7 @@ pub async fn update_avatar(
|
||||||
// Check avatar exists
|
// Check avatar exists
|
||||||
let existing = server_avatars::get_server_avatar_by_id(&mut *guard, avatar_id)
|
let existing = server_avatars::get_server_avatar_by_id(&mut *guard, avatar_id)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Avatar not found".to_string()))?;
|
.or_not_found("Avatar")?;
|
||||||
|
|
||||||
// Update the avatar
|
// Update the avatar
|
||||||
let avatar = server_avatars::update_server_avatar(&mut *guard, avatar_id, &req).await?;
|
let avatar = server_avatars::update_server_avatar(&mut *guard, avatar_id, &req).await?;
|
||||||
|
|
@ -127,7 +127,7 @@ pub async fn delete_avatar(
|
||||||
// Get the avatar first to log its name
|
// Get the avatar first to log its name
|
||||||
let avatar = server_avatars::get_server_avatar_by_id(&mut *guard, avatar_id)
|
let avatar = server_avatars::get_server_avatar_by_id(&mut *guard, avatar_id)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Avatar not found".to_string()))?;
|
.or_not_found("Avatar")?;
|
||||||
|
|
||||||
// Delete from database
|
// Delete from database
|
||||||
server_avatars::delete_server_avatar(&mut *guard, avatar_id).await?;
|
server_avatars::delete_server_avatar(&mut *guard, avatar_id).await?;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
use axum::Json;
|
use axum::Json;
|
||||||
use axum::extract::Path;
|
use axum::extract::Path;
|
||||||
use chattyness_db::{models::LooseProp, queries::loose_props};
|
use chattyness_db::{models::LooseProp, queries::loose_props};
|
||||||
use chattyness_error::AppError;
|
use chattyness_error::{AppError, OptionExt};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
|
@ -34,7 +34,7 @@ pub async fn get_loose_prop(
|
||||||
|
|
||||||
let prop = loose_props::get_loose_prop_by_id(&mut *guard, loose_prop_id)
|
let prop = loose_props::get_loose_prop_by_id(&mut *guard, loose_prop_id)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Loose prop not found or has expired".to_string()))?;
|
.or_not_found("Loose prop (may have expired)")?;
|
||||||
|
|
||||||
Ok(Json(prop))
|
Ok(Json(prop))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use chattyness_db::{
|
||||||
models::{CreateServerPropRequest, ServerProp, ServerPropSummary},
|
models::{CreateServerPropRequest, ServerProp, ServerPropSummary},
|
||||||
queries::props,
|
queries::props,
|
||||||
};
|
};
|
||||||
use chattyness_error::AppError;
|
use chattyness_error::{AppError, OptionExt};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
|
|
@ -218,7 +218,7 @@ pub async fn get_prop(
|
||||||
) -> Result<Json<ServerProp>, AppError> {
|
) -> Result<Json<ServerProp>, AppError> {
|
||||||
let prop = props::get_server_prop_by_id(&pool, prop_id)
|
let prop = props::get_server_prop_by_id(&pool, prop_id)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Prop not found".to_string()))?;
|
.or_not_found("Prop")?;
|
||||||
Ok(Json(prop))
|
Ok(Json(prop))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,7 +233,7 @@ pub async fn delete_prop(
|
||||||
// Get the prop first to get the asset path
|
// Get the prop first to get the asset path
|
||||||
let prop = props::get_server_prop_by_id(&mut *guard, prop_id)
|
let prop = props::get_server_prop_by_id(&mut *guard, prop_id)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Prop not found".to_string()))?;
|
.or_not_found("Prop")?;
|
||||||
|
|
||||||
// Delete from database
|
// Delete from database
|
||||||
props::delete_server_prop(&mut *guard, prop_id).await?;
|
props::delete_server_prop(&mut *guard, prop_id).await?;
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use chattyness_db::{
|
||||||
},
|
},
|
||||||
queries::{owner as queries, realm_avatars},
|
queries::{owner as queries, realm_avatars},
|
||||||
};
|
};
|
||||||
use chattyness_error::AppError;
|
use chattyness_error::{AppError, OptionExt};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
@ -231,7 +231,7 @@ pub async fn get_realm_avatar(
|
||||||
|
|
||||||
let avatar = realm_avatars::get_realm_avatar_by_id(&pool, avatar_id)
|
let avatar = realm_avatars::get_realm_avatar_by_id(&pool, avatar_id)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Avatar not found".to_string()))?;
|
.or_not_found("Avatar")?;
|
||||||
Ok(Json(avatar))
|
Ok(Json(avatar))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -254,7 +254,7 @@ pub async fn update_realm_avatar(
|
||||||
// Check avatar exists
|
// Check avatar exists
|
||||||
let existing = realm_avatars::get_realm_avatar_by_id(&mut *guard, avatar_id)
|
let existing = realm_avatars::get_realm_avatar_by_id(&mut *guard, avatar_id)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Avatar not found".to_string()))?;
|
.or_not_found("Avatar")?;
|
||||||
|
|
||||||
// Update the avatar
|
// Update the avatar
|
||||||
let avatar = realm_avatars::update_realm_avatar(&mut *guard, avatar_id, &req).await?;
|
let avatar = realm_avatars::update_realm_avatar(&mut *guard, avatar_id, &req).await?;
|
||||||
|
|
@ -284,7 +284,7 @@ pub async fn delete_realm_avatar(
|
||||||
// Get the avatar first to log its name
|
// Get the avatar first to log its name
|
||||||
let avatar = realm_avatars::get_realm_avatar_by_id(&mut *guard, avatar_id)
|
let avatar = realm_avatars::get_realm_avatar_by_id(&mut *guard, avatar_id)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Avatar not found".to_string()))?;
|
.or_not_found("Avatar")?;
|
||||||
|
|
||||||
// Delete from database
|
// Delete from database
|
||||||
realm_avatars::delete_realm_avatar(&mut *guard, avatar_id).await?;
|
realm_avatars::delete_realm_avatar(&mut *guard, avatar_id).await?;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use chattyness_db::{
|
||||||
models::{CreateSceneRequest, Scene, SceneSummary, UpdateSceneRequest},
|
models::{CreateSceneRequest, Scene, SceneSummary, UpdateSceneRequest},
|
||||||
queries::{realms, scenes},
|
queries::{realms, scenes},
|
||||||
};
|
};
|
||||||
use chattyness_error::AppError;
|
use chattyness_error::{AppError, OptionExt};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
@ -199,7 +199,7 @@ pub async fn get_scene(
|
||||||
) -> Result<Json<Scene>, AppError> {
|
) -> Result<Json<Scene>, AppError> {
|
||||||
let scene = scenes::get_scene_by_id(&pool, scene_id)
|
let scene = scenes::get_scene_by_id(&pool, scene_id)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Scene not found".to_string()))?;
|
.or_not_found("Scene")?;
|
||||||
Ok(Json(scene))
|
Ok(Json(scene))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -273,7 +273,7 @@ pub async fn update_scene(
|
||||||
// Get the existing scene to get realm_id
|
// Get the existing scene to get realm_id
|
||||||
let existing_scene = scenes::get_scene_by_id(&mut *guard, scene_id)
|
let existing_scene = scenes::get_scene_by_id(&mut *guard, scene_id)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Scene not found".to_string()))?;
|
.or_not_found("Scene")?;
|
||||||
|
|
||||||
// Handle clear background image
|
// Handle clear background image
|
||||||
if req.clear_background_image {
|
if req.clear_background_image {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use chattyness_db::{
|
||||||
models::{CreateSpotRequest, Spot, SpotSummary, UpdateSpotRequest},
|
models::{CreateSpotRequest, Spot, SpotSummary, UpdateSpotRequest},
|
||||||
queries::spots,
|
queries::spots,
|
||||||
};
|
};
|
||||||
use chattyness_error::AppError;
|
use chattyness_error::{AppError, OptionExt};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
@ -31,7 +31,7 @@ pub async fn get_spot(
|
||||||
) -> Result<Json<Spot>, AppError> {
|
) -> Result<Json<Spot>, AppError> {
|
||||||
let spot = spots::get_spot_by_id(&pool, spot_id)
|
let spot = spots::get_spot_by_id(&pool, spot_id)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Spot not found".to_string()))?;
|
.or_not_found("Spot")?;
|
||||||
Ok(Json(spot))
|
Ok(Json(spot))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,7 +78,7 @@ pub async fn update_spot(
|
||||||
if let Some(ref new_slug) = req.slug {
|
if let Some(ref new_slug) = req.slug {
|
||||||
let existing = spots::get_spot_by_id(&mut *guard, spot_id)
|
let existing = spots::get_spot_by_id(&mut *guard, spot_id)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Spot not found".to_string()))?;
|
.or_not_found("Spot")?;
|
||||||
|
|
||||||
if Some(new_slug.clone()) != existing.slug {
|
if Some(new_slug.clone()) != existing.slug {
|
||||||
let available =
|
let available =
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use leptos::prelude::*;
|
||||||
use leptos::task::spawn_local;
|
use leptos::task::spawn_local;
|
||||||
|
|
||||||
use crate::components::{Card, PageHeader};
|
use crate::components::{Card, PageHeader};
|
||||||
|
use crate::utils::name_to_slug;
|
||||||
|
|
||||||
/// Server avatar new page component.
|
/// Server avatar new page component.
|
||||||
#[component]
|
#[component]
|
||||||
|
|
@ -28,14 +29,7 @@ pub fn AvatarsNewPage() -> impl IntoView {
|
||||||
let new_name = event_target_value(&ev);
|
let new_name = event_target_value(&ev);
|
||||||
set_name.set(new_name.clone());
|
set_name.set(new_name.clone());
|
||||||
if slug_auto.get() {
|
if slug_auto.get() {
|
||||||
let new_slug = new_name
|
set_slug.set(name_to_slug(&new_name));
|
||||||
.to_lowercase()
|
|
||||||
.chars()
|
|
||||||
.map(|c| if c.is_alphanumeric() { c } else { '-' })
|
|
||||||
.collect::<String>()
|
|
||||||
.trim_matches('-')
|
|
||||||
.to_string();
|
|
||||||
set_slug.set(new_slug);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use leptos::prelude::*;
|
||||||
use leptos::task::spawn_local;
|
use leptos::task::spawn_local;
|
||||||
|
|
||||||
use crate::components::{Card, PageHeader};
|
use crate::components::{Card, PageHeader};
|
||||||
|
use crate::utils::name_to_slug;
|
||||||
|
|
||||||
/// Prop new page component with file upload.
|
/// Prop new page component with file upload.
|
||||||
#[component]
|
#[component]
|
||||||
|
|
@ -32,14 +33,7 @@ pub fn PropsNewPage() -> impl IntoView {
|
||||||
let new_name = event_target_value(&ev);
|
let new_name = event_target_value(&ev);
|
||||||
set_name.set(new_name.clone());
|
set_name.set(new_name.clone());
|
||||||
if slug_auto.get() {
|
if slug_auto.get() {
|
||||||
let new_slug = new_name
|
set_slug.set(name_to_slug(&new_name));
|
||||||
.to_lowercase()
|
|
||||||
.chars()
|
|
||||||
.map(|c| if c.is_alphanumeric() { c } else { '-' })
|
|
||||||
.collect::<String>()
|
|
||||||
.trim_matches('-')
|
|
||||||
.to_string();
|
|
||||||
set_slug.set(new_slug);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use leptos::task::spawn_local;
|
||||||
use leptos_router::hooks::use_params_map;
|
use leptos_router::hooks::use_params_map;
|
||||||
|
|
||||||
use crate::components::{Card, PageHeader};
|
use crate::components::{Card, PageHeader};
|
||||||
|
use crate::utils::name_to_slug;
|
||||||
#[cfg(feature = "hydrate")]
|
#[cfg(feature = "hydrate")]
|
||||||
use crate::utils::get_api_base;
|
use crate::utils::get_api_base;
|
||||||
|
|
||||||
|
|
@ -36,14 +37,7 @@ pub fn RealmAvatarsNewPage() -> impl IntoView {
|
||||||
let new_name = event_target_value(&ev);
|
let new_name = event_target_value(&ev);
|
||||||
set_name.set(new_name.clone());
|
set_name.set(new_name.clone());
|
||||||
if slug_auto.get() {
|
if slug_auto.get() {
|
||||||
let new_slug = new_name
|
set_avatar_slug.set(name_to_slug(&new_name));
|
||||||
.to_lowercase()
|
|
||||||
.chars()
|
|
||||||
.map(|c| if c.is_alphanumeric() { c } else { '-' })
|
|
||||||
.collect::<String>()
|
|
||||||
.trim_matches('-')
|
|
||||||
.to_string();
|
|
||||||
set_avatar_slug.set(new_slug);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use leptos::prelude::*;
|
||||||
use leptos::task::spawn_local;
|
use leptos::task::spawn_local;
|
||||||
|
|
||||||
use crate::components::{Card, PageHeader};
|
use crate::components::{Card, PageHeader};
|
||||||
|
use crate::utils::name_to_slug;
|
||||||
|
|
||||||
/// Realm new page component.
|
/// Realm new page component.
|
||||||
#[component]
|
#[component]
|
||||||
|
|
@ -40,14 +41,7 @@ pub fn RealmNewPage() -> impl IntoView {
|
||||||
let new_name = event_target_value(&ev);
|
let new_name = event_target_value(&ev);
|
||||||
set_name.set(new_name.clone());
|
set_name.set(new_name.clone());
|
||||||
if slug_auto.get() {
|
if slug_auto.get() {
|
||||||
let new_slug = new_name
|
set_slug.set(name_to_slug(&new_name));
|
||||||
.to_lowercase()
|
|
||||||
.chars()
|
|
||||||
.map(|c| if c.is_alphanumeric() { c } else { '-' })
|
|
||||||
.collect::<String>()
|
|
||||||
.trim_matches('-')
|
|
||||||
.to_string();
|
|
||||||
set_slug.set(new_slug);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use leptos::task::spawn_local;
|
||||||
use leptos_router::hooks::use_params_map;
|
use leptos_router::hooks::use_params_map;
|
||||||
|
|
||||||
use crate::components::{Card, PageHeader};
|
use crate::components::{Card, PageHeader};
|
||||||
|
use crate::utils::name_to_slug;
|
||||||
#[cfg(feature = "hydrate")]
|
#[cfg(feature = "hydrate")]
|
||||||
use crate::utils::fetch_image_dimensions_client;
|
use crate::utils::fetch_image_dimensions_client;
|
||||||
|
|
||||||
|
|
@ -40,14 +41,7 @@ pub fn SceneNewPage() -> impl IntoView {
|
||||||
let new_name = event_target_value(&ev);
|
let new_name = event_target_value(&ev);
|
||||||
set_name.set(new_name.clone());
|
set_name.set(new_name.clone());
|
||||||
if slug_auto.get() {
|
if slug_auto.get() {
|
||||||
let new_slug = new_name
|
set_slug.set(name_to_slug(&new_name));
|
||||||
.to_lowercase()
|
|
||||||
.chars()
|
|
||||||
.map(|c| if c.is_alphanumeric() { c } else { '-' })
|
|
||||||
.collect::<String>()
|
|
||||||
.trim_matches('-')
|
|
||||||
.to_string();
|
|
||||||
set_slug.set(new_slug);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,24 @@
|
||||||
//! Utility functions for the admin UI.
|
//! Utility functions for the admin UI.
|
||||||
|
|
||||||
|
/// Generate a URL-friendly slug from a name.
|
||||||
|
///
|
||||||
|
/// Converts to lowercase, replaces non-alphanumeric chars with hyphens,
|
||||||
|
/// and trims leading/trailing hyphens.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```rust
|
||||||
|
/// assert_eq!(name_to_slug("My Cool Realm!"), "my-cool-realm-");
|
||||||
|
/// assert_eq!(name_to_slug("Test 123"), "test-123");
|
||||||
|
/// ```
|
||||||
|
pub fn name_to_slug(name: &str) -> String {
|
||||||
|
name.to_lowercase()
|
||||||
|
.chars()
|
||||||
|
.map(|c| if c.is_alphanumeric() { c } else { '-' })
|
||||||
|
.collect::<String>()
|
||||||
|
.trim_matches('-')
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the API base path based on the current URL.
|
/// Gets the API base path based on the current URL.
|
||||||
///
|
///
|
||||||
/// Returns `/api/admin` if the current path starts with `/admin`,
|
/// Returns `/api/admin` if the current path starts with `/admin`,
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use sqlx::PgExecutor;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::models::{InventoryItem, LooseProp};
|
use crate::models::{InventoryItem, LooseProp};
|
||||||
use chattyness_error::AppError;
|
use chattyness_error::{AppError, OptionExt};
|
||||||
|
|
||||||
/// Ensure an instance exists for a scene.
|
/// Ensure an instance exists for a scene.
|
||||||
///
|
///
|
||||||
|
|
@ -330,7 +330,7 @@ pub async fn pick_up_loose_prop<'e>(
|
||||||
.bind(user_id)
|
.bind(user_id)
|
||||||
.fetch_optional(executor)
|
.fetch_optional(executor)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Loose prop not found or has expired".to_string()))?;
|
.or_not_found("Loose prop (may have expired)")?;
|
||||||
|
|
||||||
Ok(item)
|
Ok(item)
|
||||||
}
|
}
|
||||||
|
|
@ -396,7 +396,7 @@ pub async fn update_loose_prop_scale<'e>(
|
||||||
.bind(scale)
|
.bind(scale)
|
||||||
.fetch_optional(executor)
|
.fetch_optional(executor)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Loose prop not found or has expired".to_string()))?;
|
.or_not_found("Loose prop (may have expired)")?;
|
||||||
|
|
||||||
Ok(prop)
|
Ok(prop)
|
||||||
}
|
}
|
||||||
|
|
@ -490,7 +490,7 @@ pub async fn move_loose_prop<'e>(
|
||||||
.bind(y as f32)
|
.bind(y as f32)
|
||||||
.fetch_optional(executor)
|
.fetch_optional(executor)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Loose prop not found or has expired".to_string()))?;
|
.or_not_found("Loose prop (may have expired)")?;
|
||||||
|
|
||||||
Ok(prop)
|
Ok(prop)
|
||||||
}
|
}
|
||||||
|
|
@ -546,7 +546,7 @@ pub async fn lock_loose_prop<'e>(
|
||||||
.bind(locked_by)
|
.bind(locked_by)
|
||||||
.fetch_optional(executor)
|
.fetch_optional(executor)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Loose prop not found or has expired".to_string()))?;
|
.or_not_found("Loose prop (may have expired)")?;
|
||||||
|
|
||||||
Ok(prop)
|
Ok(prop)
|
||||||
}
|
}
|
||||||
|
|
@ -600,7 +600,7 @@ pub async fn unlock_loose_prop<'e>(
|
||||||
.bind(loose_prop_id)
|
.bind(loose_prop_id)
|
||||||
.fetch_optional(executor)
|
.fetch_optional(executor)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Loose prop not found or has expired".to_string()))?;
|
.or_not_found("Loose prop (may have expired)")?;
|
||||||
|
|
||||||
Ok(prop)
|
Ok(prop)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use sqlx::PgExecutor;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::models::{CreateSceneRequest, Scene, SceneSummary, UpdateSceneRequest};
|
use crate::models::{CreateSceneRequest, Scene, SceneSummary, UpdateSceneRequest};
|
||||||
use chattyness_error::AppError;
|
use chattyness_error::{AppError, OptionExt};
|
||||||
|
|
||||||
/// List all scenes for a realm.
|
/// List all scenes for a realm.
|
||||||
pub async fn list_scenes_for_realm<'e>(
|
pub async fn list_scenes_for_realm<'e>(
|
||||||
|
|
@ -374,7 +374,7 @@ pub async fn update_scene<'e>(
|
||||||
let scene = query_builder
|
let scene = query_builder
|
||||||
.fetch_optional(executor)
|
.fetch_optional(executor)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Scene not found".to_string()))?;
|
.or_not_found("Scene")?;
|
||||||
|
|
||||||
Ok(scene)
|
Ok(scene)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use sqlx::PgExecutor;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::models::{CreateSpotRequest, Spot, SpotSummary, UpdateSpotRequest};
|
use crate::models::{CreateSpotRequest, Spot, SpotSummary, UpdateSpotRequest};
|
||||||
use chattyness_error::AppError;
|
use chattyness_error::{AppError, OptionExt};
|
||||||
|
|
||||||
/// List all spots for a scene.
|
/// List all spots for a scene.
|
||||||
pub async fn list_spots_for_scene<'e>(
|
pub async fn list_spots_for_scene<'e>(
|
||||||
|
|
@ -289,7 +289,7 @@ pub async fn update_spot<'e>(
|
||||||
let spot = query_builder
|
let spot = query_builder
|
||||||
.fetch_optional(executor)
|
.fetch_optional(executor)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Spot not found".to_string()))?;
|
.or_not_found("Spot")?;
|
||||||
|
|
||||||
Ok(spot)
|
Ok(spot)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,27 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// Extension trait for Option to convert to AppError::NotFound.
|
||||||
|
///
|
||||||
|
/// Reduces boilerplate when fetching entities that may not exist.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```rust
|
||||||
|
/// use chattyness_error::OptionExt;
|
||||||
|
///
|
||||||
|
/// let scene = get_scene_by_id(&pool, id).await?.or_not_found("Scene")?;
|
||||||
|
/// ```
|
||||||
|
pub trait OptionExt<T> {
|
||||||
|
/// Convert None to AppError::NotFound with a descriptive message.
|
||||||
|
fn or_not_found(self, entity: &str) -> Result<T, AppError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> OptionExt<T> for Option<T> {
|
||||||
|
fn or_not_found(self, entity: &str) -> Result<T, AppError> {
|
||||||
|
self.ok_or_else(|| AppError::NotFound(format!("{} not found", entity)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Application error types for chattyness.
|
/// Application error types for chattyness.
|
||||||
///
|
///
|
||||||
/// All errors derive From for automatic conversion where applicable.
|
/// All errors derive From for automatic conversion where applicable.
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use chattyness_db::{
|
||||||
models::{Scene, SceneSummary, Spot, SpotSummary},
|
models::{Scene, SceneSummary, Spot, SpotSummary},
|
||||||
queries::{realms, scenes, spots},
|
queries::{realms, scenes, spots},
|
||||||
};
|
};
|
||||||
use chattyness_error::AppError;
|
use chattyness_error::{AppError, OptionExt};
|
||||||
|
|
||||||
/// Get the entry scene for a realm.
|
/// Get the entry scene for a realm.
|
||||||
///
|
///
|
||||||
|
|
@ -86,7 +86,7 @@ pub async fn get_spot(
|
||||||
) -> Result<Json<Spot>, AppError> {
|
) -> Result<Json<Spot>, AppError> {
|
||||||
let spot = spots::get_spot_by_id(&pool, spot_id)
|
let spot = spots::get_spot_by_id(&pool, spot_id)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Spot not found".to_string()))?;
|
.or_not_found("Spot")?;
|
||||||
|
|
||||||
Ok(Json(spot))
|
Ok(Json(spot))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue