use anyhow::{Context, Result}; use chrono::NaiveDateTime; use serde::{Deserialize, Serialize}; use tracing::instrument; #[derive(Debug, Serialize, Deserialize)] pub struct File { pub id: i32, pub name: String, pub file_name: String, pub nexus_file_id: i32, pub mod_id: i32, pub category: Option, pub version: Option, pub mod_version: Option, pub size: i64, pub uploaded_at: NaiveDateTime, pub has_download_link: bool, pub updated_at: NaiveDateTime, pub created_at: NaiveDateTime, pub downloaded_at: Option, pub has_plugin: bool, pub unable_to_extract_plugins: bool, } #[derive(Debug)] pub struct UnsavedFile<'a> { pub name: &'a str, pub file_name: &'a str, pub nexus_file_id: i32, pub mod_id: i32, pub category: Option<&'a str>, pub version: Option<&'a str>, pub mod_version: Option<&'a str>, pub size: i64, pub uploaded_at: NaiveDateTime, } #[instrument(level = "debug", skip(pool))] pub async fn get_by_nexus_file_id( pool: &sqlx::Pool, nexus_file_id: i32, ) -> Result> { sqlx::query_as!( File, "SELECT * FROM files WHERE nexus_file_id = $1", nexus_file_id, ) .fetch_optional(pool) .await .context("Failed to get file") } #[instrument(level = "debug", skip(pool))] pub async fn get_processed_nexus_file_ids_by_mod_id( pool: &sqlx::Pool, mod_id: i32, ) -> Result> { sqlx::query!( "SELECT nexus_file_id FROM files WHERE mod_id = $1 AND ( downloaded_at IS NOT NULL OR has_plugin = false OR has_download_link = false )", mod_id ) .map(|row| row.nexus_file_id) .fetch_all(pool) .await .context("Failed to get files") } #[instrument(level = "debug", skip(pool))] pub async fn insert<'a>( pool: &sqlx::Pool, unsaved_file: &UnsavedFile<'a>, ) -> Result { sqlx::query_as!( File, "INSERT INTO files (name, file_name, nexus_file_id, mod_id, category, version, mod_version, size, uploaded_at, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, now(), now()) ON CONFLICT (mod_id, nexus_file_id) DO UPDATE SET (name, file_name, category, version, mod_version, uploaded_at, updated_at) = (EXCLUDED.name, EXCLUDED.file_name, EXCLUDED.category, EXCLUDED.version, EXCLUDED.mod_version, EXCLUDED.uploaded_at, now()) RETURNING *", unsaved_file.name, unsaved_file.file_name, unsaved_file.nexus_file_id, unsaved_file.mod_id, unsaved_file.category, unsaved_file.version, unsaved_file.mod_version, unsaved_file.size, unsaved_file.uploaded_at ) .fetch_one(pool) .await .context("Failed to insert file") } #[instrument(level = "debug", skip(pool))] pub async fn update_has_download_link( pool: &sqlx::Pool, id: i32, has_download_link: bool, ) -> Result { sqlx::query_as!( File, "UPDATE files SET has_download_link = $2 WHERE id = $1 RETURNING *", id, has_download_link, ) .fetch_one(pool) .await .context("Failed to update file") } #[instrument(level = "debug", skip(pool))] pub async fn update_downloaded_at(pool: &sqlx::Pool, id: i32) -> Result { sqlx::query_as!( File, "UPDATE files SET downloaded_at = now() WHERE id = $1 RETURNING *", id, ) .fetch_one(pool) .await .context("Failed to update file") } #[instrument(level = "debug", skip(pool))] pub async fn update_has_plugin( pool: &sqlx::Pool, id: i32, has_plugin: bool, ) -> Result { sqlx::query_as!( File, "UPDATE files SET has_plugin = $2 WHERE id = $1 RETURNING *", id, has_plugin, ) .fetch_one(pool) .await .context("Failed to update file") } #[instrument(level = "debug", skip(pool))] pub async fn update_unable_to_extract_plugins( pool: &sqlx::Pool, id: i32, unable_to_extract_plugins: bool, ) -> Result { sqlx::query_as!( File, "UPDATE files SET unable_to_extract_plugins = $2 WHERE id = $1 RETURNING *", id, unable_to_extract_plugins, ) .fetch_one(pool) .await .context("Failed to update file") }