summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/db/game.rs6
-rw-r--r--src/db/mod.rs2
-rw-r--r--src/db/product.rs44
-rw-r--r--src/db/story.rs26
-rw-r--r--src/db/tool.rs6
-rw-r--r--src/handlers/frontend_builder.rs108
-rw-r--r--src/handlers/mod.rs10
7 files changed, 96 insertions, 106 deletions
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<Vec<Self>, AppError>
+ async fn get_all(pool: &SqlitePool) -> Result<Vec<Self>, 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<T: ProductMarker> {
+ pub product: Product,
+ pub detail: T,
+}
+
+async fn get_all_products(pool: &SqlitePool) -> Result<Vec<Product>, AppError> {
+ Ok(sqlx::query_as!(Product, "SELECT * FROM product")
+ .fetch_all(pool)
+ .await?)
+}
+
+pub async fn get_all_specificproducts<T: ProductMarker + ProductDatabaseHandler>(
+ pool: &SqlitePool,
+) -> Result<Vec<SpecificProduct<T>>, AppError> {
+ let product_details = T::get_all(pool).await?;
+ let products = get_all_products(pool).await?;
+ let mut details_map: HashMap<i64, T> = 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<Vec<Self>, AppError>
+ async fn get_all(pool: &SqlitePool) -> Result<Vec<Self>, 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<impl IntoResponse, AppError> {
@@ -64,7 +66,7 @@ fn card<T: PageBuilder + ProductMarker>(specific_product: SpecificProduct<T>) ->
(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<T: Sized + ProductMarker + PageBuilder>(
+pub async fn build_page<T: Sized + ProductMarker + PageBuilder + ProductDatabaseHandler>(
+ ctx: Extension<super::ApiContext>,
) -> Result<impl IntoResponse, AppError> {
Ok(html!(
- (page(T::page_title(), cards(T::products(), T::page_title())))
+ (page(
+ T::page_title(),
+ cards(
+ get_all_specificproducts::<T>(&ctx.pool).await?,
+ T::page_title()
+ )
+ ))
))
}
pub trait PageBuilder {
fn page_title() -> String;
- fn products() -> Vec<SpecificProduct<Self>>
- 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<SpecificProduct<Self>>
- 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<T: ProductMarker> {
- 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::<DummyProduct>))
+ .route("/stories", get(build_page::<Story>))
.nest_service("/static", ServeDir::new("static"))
.layer(DefaultBodyLimit::max(1074000000))
.layer(AddExtensionLayer::new(ApiContext { pool }))