From fa7f650562337c7f3a4fb7446e56575e26380494 Mon Sep 17 00:00:00 2001 From: WriterV2 Date: Sun, 3 Nov 2024 18:50:29 +0100 Subject: Feat: Finalize ability to get all products with details - Remove dummy product - Add ProductMarker method to get product id + implement - Move method to get all products out of PageBuilder into its own function in Product module - Finalize getting all products with details - Add empty implementation for Story for testing purposes (TODO: add card details) --- src/db/game.rs | 6 ++- src/db/mod.rs | 2 +- src/db/product.rs | 44 +++++++++++++++- src/db/story.rs | 26 ++++++++-- src/db/tool.rs | 6 ++- src/handlers/frontend_builder.rs | 108 +++++---------------------------------- src/handlers/mod.rs | 10 ++-- 7 files changed, 96 insertions(+), 106 deletions(-) (limited to 'src') diff --git a/src/db/game.rs b/src/db/game.rs index 0620314..e10006c 100644 --- a/src/db/game.rs +++ b/src/db/game.rs @@ -7,4 +7,8 @@ pub struct Game { pub pid: i64, } -impl ProductMarker for Game {} +impl ProductMarker for Game { + fn product_id(&self) -> i64 { + self.pid + } +} diff --git a/src/db/mod.rs b/src/db/mod.rs index 1d64ed8..0b310c6 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -14,7 +14,7 @@ pub mod tooltag; #[async_trait] pub trait ProductDatabaseHandler { - async fn get_all(pool: SqlitePool) -> Result, AppError> + async fn get_all(pool: &SqlitePool) -> Result, AppError> where Self: Sized; diff --git a/src/db/product.rs b/src/db/product.rs index 09b0dff..0fc8f05 100644 --- a/src/db/product.rs +++ b/src/db/product.rs @@ -1,3 +1,11 @@ +use std::collections::HashMap; + +use sqlx::SqlitePool; + +use crate::error::AppError; + +use super::ProductDatabaseHandler; + #[derive(Debug, serde::Serialize, sqlx::FromRow)] pub struct Product { pub id: i64, @@ -7,4 +15,38 @@ pub struct Product { pub updatedate: i64, } -pub trait ProductMarker {} +pub trait ProductMarker { + fn product_id(&self) -> i64; +} + +pub struct SpecificProduct { + pub product: Product, + pub detail: T, +} + +async fn get_all_products(pool: &SqlitePool) -> Result, AppError> { + Ok(sqlx::query_as!(Product, "SELECT * FROM product") + .fetch_all(pool) + .await?) +} + +pub async fn get_all_specificproducts( + pool: &SqlitePool, +) -> Result>, AppError> { + let product_details = T::get_all(pool).await?; + let products = get_all_products(pool).await?; + let mut details_map: HashMap = product_details + .into_iter() + .map(|detail| (detail.product_id(), detail)) + .collect(); + + let specific_products = products + .into_iter() + .filter_map(|product| { + details_map + .remove(&product.id) + .map(|detail| SpecificProduct { product, detail }) + }) + .collect(); + Ok(specific_products) +} diff --git a/src/db/story.rs b/src/db/story.rs index ee79e4f..686c33f 100644 --- a/src/db/story.rs +++ b/src/db/story.rs @@ -1,8 +1,10 @@ use axum::async_trait; +use maud::html; use sqlx::SqlitePool; use crate::error::AppError; use crate::db::product::Product; +use crate::handlers::frontend_builder::PageBuilder; use super::product::ProductMarker; use super::ProductDatabaseHandler; @@ -17,16 +19,20 @@ pub struct Story { pub pid: i64, } -impl ProductMarker for Story {} +impl ProductMarker for Story { + fn product_id(&self) -> i64 { + self.pid + } +} #[async_trait] impl ProductDatabaseHandler for Story { - async fn get_all(pool: SqlitePool) -> Result, AppError> + async fn get_all(pool: &SqlitePool) -> Result, AppError> where Self: Sized, { Ok(sqlx::query_as!(Story, "SELECT * FROM story") - .fetch_all(&pool) + .fetch_all(pool) .await?) } @@ -63,3 +69,17 @@ impl ProductDatabaseHandler for Story { Ok(story) } } + +impl PageBuilder for Story { + fn page_title() -> String { + "Stories".to_string() + } + + // TODO: Add pdf and epub download link + language info + fn product_specific_card_content(&self) -> maud::Markup { + html!( + span { + } + ) + } +} diff --git a/src/db/tool.rs b/src/db/tool.rs index 5f23f54..c53dc1e 100644 --- a/src/db/tool.rs +++ b/src/db/tool.rs @@ -7,4 +7,8 @@ pub struct Tool { pub pid: i64, } -impl ProductMarker for Tool {} +impl ProductMarker for Tool { + fn product_id(&self) -> i64 { + self.id + } +} diff --git a/src/handlers/frontend_builder.rs b/src/handlers/frontend_builder.rs index 8e3a9f2..bac9f22 100644 --- a/src/handlers/frontend_builder.rs +++ b/src/handlers/frontend_builder.rs @@ -1,7 +1,9 @@ use axum::response::IntoResponse; +use axum::Extension; use maud::{html, Markup, DOCTYPE}; -use crate::db::product::{Product, ProductMarker}; +use crate::db::product::{get_all_specificproducts, ProductMarker, SpecificProduct}; +use crate::db::ProductDatabaseHandler; use crate::error::AppError; pub async fn home() -> Result { @@ -64,7 +66,7 @@ fn card(specific_product: SpecificProduct) -> (specific_product.product.description) } div { - (specific_product.specific.product_specific_card_content()) + (specific_product.detail.product_specific_card_content()) } div class="flex flex-row space-x-5 mt-9 text-xs" { span {"Update: " (specific_product.product.updatedate)} @@ -85,105 +87,21 @@ fn head(page_title: String) -> Markup { } } -pub async fn build_page( +pub async fn build_page( + ctx: Extension, ) -> Result { Ok(html!( - (page(T::page_title(), cards(T::products(), T::page_title()))) + (page( + T::page_title(), + cards( + get_all_specificproducts::(&ctx.pool).await?, + T::page_title() + ) + )) )) } pub trait PageBuilder { fn page_title() -> String; - fn products() -> Vec> - where - Self: Sized + ProductMarker; fn product_specific_card_content(&self) -> Markup; } - -// TODO: Remove after first implementation -#[derive(Hash, PartialEq, Eq)] -pub struct DummyProduct { - dummy: String, -} - -impl PageBuilder for DummyProduct { - fn page_title() -> String { - "Dummy Products".to_string() - } - - fn products() -> Vec> - where - Self: Sized, - { - let product1 = Product { - id: 0, - name: "Willensfreiheit in Ketten".to_string(), - description: - "Nach der Flucht seines Bruders, verzweifelt Fahim an der Existenz der Willensfreiheit." - .to_string(), - uploaddate: 1, - updatedate: 1, - }; - - let product2 = Product { - id: 1, - name: "Aufbruch und Abbruch".to_string(), - description: "Ein frischer Absolvent muss sich vor der ersten Quest nur noch bei der Gilde für Heilkünstler anmelden.".to_string(), - uploaddate: 1, - updatedate: 1, - }; - - let product3 = Product { - id: 2, - name: "Mit gutem Gewissen".to_string(), - description: "Ein frischer Absolvent muss sich vor der ersten Quest nur noch bei der Gilde für Heilkünstler anmelden.".to_string(), - uploaddate: 1, - updatedate: 1, - }; - - let test1 = DummyProduct { - dummy: "/".to_string(), - }; - - let test2 = DummyProduct { - dummy: "/".to_string(), - }; - - let test3 = DummyProduct { - dummy: "/".to_string(), - }; - - let specific1 = SpecificProduct { - product: product1, - specific: test1, - }; - - let specific2 = SpecificProduct { - product: product2, - specific: test2, - }; - - let specific3 = SpecificProduct { - product: product3, - specific: test3, - }; - - vec![specific1, specific2, specific3] - } - - fn product_specific_card_content(&self) -> Markup { - html!( - a class="mt-9 text-[#601B69] dark:text-[#E1B1E7]" href=(self.dummy) { - "Link" - } - ) - } -} - -impl ProductMarker for DummyProduct {} - -// TODO: Refactoring -pub struct SpecificProduct { - product: Product, - specific: T, -} diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index 34481e7..e2063eb 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -12,13 +12,15 @@ use tower_http::services::ServeDir; use admin::{admin_healthcheck, admin_middleware, generate_token, upload_story}; use frontend_builder::home; -use self::frontend_builder::{build_page, DummyProduct}; +use crate::db::story::Story; + +use self::frontend_builder::build_page; mod admin; -mod frontend_builder; +pub mod frontend_builder; #[derive(Debug, Clone)] -struct ApiContext { +pub struct ApiContext { pool: SqlitePool, } @@ -41,7 +43,7 @@ pub async fn router(pool: SqlitePool) -> Router { )) .with_state(state) .route("/", get(home)) - .route("/stories", get(build_page::)) + .route("/stories", get(build_page::)) .nest_service("/static", ServeDir::new("static")) .layer(DefaultBodyLimit::max(1074000000)) .layer(AddExtensionLayer::new(ApiContext { pool })) -- cgit v1.2.3