summaryrefslogtreecommitdiff
path: root/src/db
diff options
context:
space:
mode:
Diffstat (limited to 'src/db')
-rw-r--r--src/db/game.rs14
-rw-r--r--src/db/mod.rs32
-rw-r--r--src/db/product.rs110
-rw-r--r--src/db/producttag.rs5
-rw-r--r--src/db/story.rs129
-rw-r--r--src/db/tag.rs5
-rw-r--r--src/db/tool.rs14
7 files changed, 0 insertions, 309 deletions
diff --git a/src/db/game.rs b/src/db/game.rs
deleted file mode 100644
index e10006c..0000000
--- a/src/db/game.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-use super::product::ProductMarker;
-
-#[derive(Debug, serde::Serialize, sqlx::FromRow)]
-pub struct Game {
- pub id: i64,
- pub repolink: String,
- pub pid: i64,
-}
-
-impl ProductMarker for Game {
- fn product_id(&self) -> i64 {
- self.pid
- }
-}
diff --git a/src/db/mod.rs b/src/db/mod.rs
deleted file mode 100644
index f1baf44..0000000
--- a/src/db/mod.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-use sqlx::SqlitePool;
-
-use crate::error::AppError;
-use async_trait::async_trait;
-
-pub mod game;
-pub mod product;
-pub mod producttag;
-pub mod story;
-pub mod tag;
-pub mod tool;
-
-#[async_trait]
-pub trait ProductDatabaseHandler {
- async fn get_all(pool: &SqlitePool) -> Result<Vec<Self>, AppError>
- where
- Self: Sized;
-
- async fn post(
- &self,
- pool: &SqlitePool,
- name: &str,
- description: &str,
- tags: Vec<&str>,
- ) -> Result<Self, AppError>
- where
- Self: Sized;
-
- async fn delete(pool: &SqlitePool, id: i64) -> Result<(), AppError>
- where
- Self: Sized;
-}
diff --git a/src/db/product.rs b/src/db/product.rs
deleted file mode 100644
index e5e797f..0000000
--- a/src/db/product.rs
+++ /dev/null
@@ -1,110 +0,0 @@
-use std::collections::HashMap;
-use std::time::SystemTime;
-
-use sqlx::{QueryBuilder, Sqlite, SqlitePool, Transaction};
-
-use crate::error::AppError;
-
-use super::tag::Tag;
-use super::ProductDatabaseHandler;
-
-#[derive(Debug, serde::Serialize, sqlx::FromRow)]
-pub struct Product {
- pub id: i64,
- pub name: String,
- pub description: String,
- pub uploaddate: i64,
- pub updatedate: i64,
-}
-
-impl Product {
- pub async fn post(tx: &mut Transaction<'static, Sqlite>, name: &str, description: &str, tags: Vec<&str>) -> Result<Product, AppError> {
- let now = SystemTime::now().duration_since(std::time::UNIX_EPOCH)?.as_millis() as i64;
- let product = sqlx::query_as!(
- Product,
- "INSERT INTO product (name, description, uploaddate, updatedate) VALUES ($1, $2, $3, $4) RETURNING id, name, description, uploaddate, updatedate",
- name,
- description,
- now,
- now)
- .fetch_one(&mut **tx)
- .await?;
-
- if !tags.is_empty() {
- // insert tags that do not exist yet
- let mut tags_query_builder: QueryBuilder<Sqlite> = QueryBuilder::new("INSERT INTO tag (name) ");
- tags_query_builder.push_values(tags.iter(), | mut b, tag | { b.push_bind(tag); });
- tags_query_builder.push(" ON CONFLICT(name) DO NOTHING;");
- tags_query_builder.build().execute(&mut **tx).await?;
-
- // fetch all tags of story
- let added_tags: Vec<Tag> = {
- let mut tags_fetch_query_builder = QueryBuilder::new("WITH input(name) AS (");
- tags_fetch_query_builder.push_values(tags.iter(), |mut b, tag| {
- b.push_bind(tag);
- });
- tags_fetch_query_builder.push(
- ") \
- SELECT t.id, t.name \
- FROM tag t \
- JOIN input i ON i.name = t.name;"
- );
- tags_fetch_query_builder.build_query_as::<Tag>().fetch_all(&mut **tx).await?
- };
- println!("{:?}", added_tags);
-
- // bind tags to story if they are not bound yet
- let mut producttags_query_builder: QueryBuilder<Sqlite> = QueryBuilder::new("INSERT INTO producttag (pid, tid) ");
- producttags_query_builder.push_values(added_tags.iter(), | mut b, added_tag | {
- b.push_bind(product.id).push_bind(added_tag.id);
- });
- producttags_query_builder.push(" ON CONFLICT(pid, tid) DO NOTHING;");
- producttags_query_builder
- .build().execute(&mut **tx).await?;
-
- }
- Ok(product)
- }
-
- pub async fn delete(tx: &mut Transaction<'static, Sqlite>, pid: i64) -> Result<(), AppError> {
- sqlx::query!("DELETE FROM producttag WHERE pid = $1", pid).execute(&mut **tx).await?;
- sqlx::query!("DELETE FROM product WHERE id = $1", pid).execute(&mut **tx).await?;
- Ok(())
- }
-}
-
-pub trait ProductMarker {
- fn product_id(&self) -> i64;
-}
-
-pub struct SpecificProduct<T: ProductMarker> {
- pub product: Product,
- pub detail: T,
-}
-
-pub 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/producttag.rs b/src/db/producttag.rs
deleted file mode 100644
index 160e720..0000000
--- a/src/db/producttag.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-#[derive(Debug, serde::Serialize, sqlx::FromRow)]
-pub struct ProductTag {
- pub pid: i64,
- pub tid: i64,
-}
diff --git a/src/db/story.rs b/src/db/story.rs
deleted file mode 100644
index 1566d22..0000000
--- a/src/db/story.rs
+++ /dev/null
@@ -1,129 +0,0 @@
-use std::ffi::OsStr;
-use std::fs::{read_dir, remove_file};
-use std::io::{BufWriter, Write};
-
-use anyhow::Context;
-use axum::async_trait;
-use maud::html;
-use sqlx::SqlitePool;
-
-use crate::db::product::Product;
-use crate::error::AppError;
-use crate::handlers::frontend_builder::PageBuilder;
-
-use super::product::ProductMarker;
-use super::ProductDatabaseHandler;
-
-#[derive(Debug, serde::Serialize, sqlx::FromRow)]
-pub struct Story {
- pub id: i64,
- pub language: String,
- pub pdf: Vec<u8>,
- pub pid: i64,
-}
-
-impl ProductMarker for Story {
- fn product_id(&self) -> i64 {
- self.pid
- }
-}
-
-pub async fn synchronize_story_files(pool: &SqlitePool) -> Result<(), anyhow::Error> {
- for entry in read_dir("static").with_context(|| "Failed to read directory")? {
- let path = entry.with_context(|| "Failed to get entry")?.path();
- if path.extension().is_some_and(|ext| ext == OsStr::new("pdf")) {
- remove_file(path).with_context(|| "Failed to remove file")?;
- }
- }
- let results =
- sqlx::query!("SELECT p.name, s.pdf FROM story s INNER JOIN product p ON s.pid = p.id")
- .fetch_all(pool)
- .await?;
- for result in results.iter() {
- let pdf_filename = format_filepath(&result.name, "pdf");
- let file = std::fs::File::create_new(&pdf_filename)
- .with_context(|| format!("Failed to create {}", &pdf_filename))?;
- BufWriter::new(file)
- .write_all(&result.pdf.to_vec())
- .with_context(|| format!("Failed to write {}", &pdf_filename))?;
- }
- Ok(())
-}
-
-#[async_trait]
-impl ProductDatabaseHandler for Story {
- async fn get_all(pool: &SqlitePool) -> Result<Vec<Self>, AppError>
- where
- Self: Sized,
- {
- Ok(sqlx::query_as!(Story, "SELECT * FROM story")
- .fetch_all(pool)
- .await?)
- }
-
- async fn post(
- &self,
- pool: &SqlitePool,
- name: &str,
- description: &str,
- tags: Vec<&str>,
- ) -> Result<Self, AppError>
- where
- Self: Sized,
- {
- let mut tx = pool.begin().await?;
- let product = Product::post(&mut tx, name, description, tags).await?;
- let story = sqlx::query_as!(
- Story,
- "INSERT INTO story (language, pdf, pid) VALUES ($1, $2, $3) RETURNING id, language, pdf, pid",
- self.language,
- self.pdf,
- product.id)
- .fetch_one(&mut *tx)
- .await?;
-
- tx.commit().await?;
-
- Ok(story)
- }
-
- async fn delete(pool: &SqlitePool, pid: i64) -> Result<(), AppError>
- where
- Self: Sized,
- {
- let mut tx = pool.begin().await?;
- sqlx::query!("DELETE FROM story WHERE pid = $1", pid)
- .execute(&mut *tx)
- .await?;
- Product::delete(&mut tx, pid).await?;
- tx.commit().await?;
- Ok(())
- }
-}
-
-impl<'a> PageBuilder<'a> for Story {
- fn page_title() -> &'a str {
- "Stories"
- }
-
- // TODO: Add pdf icon
- fn product_specific_card_content<T: PageBuilder<'a> + ProductMarker>(
- &self,
- specific_product: &super::product::SpecificProduct<T>,
- ) -> maud::Markup {
- html!(
- div class="flex justify-start mt-5 text-sm" {
- span class="mr-10" { (&self.language) }
- div class="flex justify-between" {
- a href=(format_filepath(&specific_product.product.name, "pdf")) { "PDF" }
- }
- }
- )
- }
-}
-
-pub fn format_filepath(name: &str, extension: &str) -> String {
- let mut filename = format!("static/{}.{}", name, extension).to_lowercase();
- filename.retain(|c| !c.is_whitespace());
- filename
-}
diff --git a/src/db/tag.rs b/src/db/tag.rs
deleted file mode 100644
index df55a8a..0000000
--- a/src/db/tag.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-#[derive(Debug, serde::Serialize, sqlx::FromRow)]
-pub struct Tag {
- pub id: i64,
- pub name: String,
-}
diff --git a/src/db/tool.rs b/src/db/tool.rs
deleted file mode 100644
index c53dc1e..0000000
--- a/src/db/tool.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-use super::product::ProductMarker;
-
-#[derive(Debug, serde::Serialize, sqlx::FromRow)]
-pub struct Tool {
- pub id: i64,
- pub repolink: String,
- pub pid: i64,
-}
-
-impl ProductMarker for Tool {
- fn product_id(&self) -> i64 {
- self.id
- }
-}