diff --git a/src/main.rs b/src/main.rs index e6a7a6c..eaa31d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,261 +24,19 @@ use tokio_util::compat::FuturesAsyncReadCompatExt; use unrar::Archive; use zip::write::{FileOptions, ZipWriter}; +mod models; + +use models::cell::insert_cell; +use models::file::{insert_file, File}; +use models::game::insert_game; +use models::game_mod::{get_mod_by_nexus_mod_id, insert_mod, Mod}; +use models::plugin::insert_plugin; +use models::plugin_cell::insert_plugin_cell; + static USER_AGENT: &str = "mod-mapper/0.1"; static GAME_NAME: &str = "skyrimspecialedition"; const GAME_ID: u32 = 1704; -#[derive(Debug, Serialize, Deserialize)] -struct Game { - id: i32, - name: String, - nexus_game_id: i32, - updated_at: NaiveDateTime, - created_at: NaiveDateTime, -} - -#[derive(Debug, Serialize, Deserialize)] -struct Mod { - id: i32, - name: String, - nexus_mod_id: i32, - author: String, - category: String, - description: Option, - game_id: i32, - updated_at: NaiveDateTime, - created_at: NaiveDateTime, -} - -#[derive(Debug, Serialize, Deserialize)] -struct File { - id: i32, - name: String, - file_name: String, - nexus_file_id: i32, - mod_id: i32, - category: Option, - version: Option, - mod_version: Option, - uploaded_at: NaiveDateTime, - updated_at: NaiveDateTime, - created_at: NaiveDateTime, -} - -#[derive(Debug, Serialize, Deserialize)] -struct Plugin { - id: i32, - name: String, - hash: i64, - file_id: i32, - version: Option, - author: Option, - description: Option, - masters: Option>, - updated_at: NaiveDateTime, - created_at: NaiveDateTime, -} - -#[derive(Debug, Serialize, Deserialize)] -struct Cell { - id: i32, - form_id: i32, - x: Option, - y: Option, - is_persistent: bool, - updated_at: NaiveDateTime, - created_at: NaiveDateTime, -} - -#[derive(Debug, Serialize, Deserialize)] -struct PluginCell { - id: i32, - plugin_id: i32, - cell_id: i32, - editor_id: Option, - updated_at: NaiveDateTime, - created_at: NaiveDateTime, -} - -async fn insert_game( - pool: &sqlx::Pool, - name: &str, - nexus_game_id: i32, -) -> Result { - sqlx::query_as!( - Game, - "INSERT INTO games - (name, nexus_game_id, created_at, updated_at) - VALUES ($1, $2, now(), now()) - ON CONFLICT (nexus_game_id, name) DO UPDATE SET (name, updated_at) = (EXCLUDED.name, now()) - RETURNING *", - name, - nexus_game_id - ) - .fetch_one(pool) - .await - .context("Failed to insert game") -} - -async fn get_mod_by_nexus_mod_id( - pool: &sqlx::Pool, - nexus_mod_id: i32, -) -> Result> { - sqlx::query_as!( - Mod, - "SELECT * FROM mods WHERE nexus_mod_id = $1", - nexus_mod_id, - ) - .fetch_optional(pool) - .await - .context("Failed to get mod") -} - -async fn insert_mod( - pool: &sqlx::Pool, - name: &str, - nexus_mod_id: i32, - author: &str, - category: &str, - description: Option<&str>, - game_id: i32, -) -> Result { - sqlx::query_as!( - Mod, - "INSERT INTO mods - (name, nexus_mod_id, author, category, description, game_id, created_at, updated_at) - VALUES ($1, $2, $3, $4, $5, $6, now(), now()) - ON CONFLICT (game_id, nexus_mod_id) DO UPDATE - SET (name, author, category, description, updated_at) = - (EXCLUDED.name, EXCLUDED.author, EXCLUDED.category, EXCLUDED.description, now()) - RETURNING *", - name, - nexus_mod_id, - author, - category, - description, - game_id - ) - .fetch_one(pool) - .await - .context("Failed to insert or update mod") -} - -async fn insert_file( - pool: &sqlx::Pool, - name: &str, - file_name: &str, - nexus_file_id: i32, - mod_id: i32, - category: Option<&str>, - version: Option<&str>, - mod_version: Option<&str>, - uploaded_at: NaiveDateTime, -) -> Result { - sqlx::query_as!( - File, - "INSERT INTO files - (name, file_name, nexus_file_id, mod_id, category, version, mod_version, uploaded_at, created_at, updated_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 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 *", - name, - file_name, - nexus_file_id, - mod_id, - category, - version, - mod_version, - uploaded_at - ) - .fetch_one(pool) - .await - .context("Failed to insert file") -} - -async fn insert_plugin( - pool: &sqlx::Pool, - name: &str, - hash: i64, - file_id: i32, - version: Option, - author: Option<&str>, - description: Option<&str>, - masters: Option<&[String]>, -) -> Result { - sqlx::query_as!( - Plugin, - "INSERT INTO plugins - (name, hash, file_id, version, author, description, masters, created_at, updated_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, now(), now()) - ON CONFLICT (file_id, name) DO UPDATE - SET (hash, version, author, description, masters, updated_at) = - (EXCLUDED.hash, EXCLUDED.version, EXCLUDED.author, EXCLUDED.description, EXCLUDED.masters, now()) - RETURNING *", - name, - hash, - file_id, - version, - author, - description, - masters - ) - .fetch_one(pool) - .await - .context("Failed to insert plugin") -} - -async fn insert_cell( - pool: &sqlx::Pool, - form_id: i32, - x: Option, - y: Option, - is_persistent: bool, -) -> Result { - sqlx::query_as!( - Cell, - "INSERT INTO cells - (form_id, x, y, is_persistent, created_at, updated_at) - VALUES ($1, $2, $3, $4, now(), now()) - ON CONFLICT (form_id) DO UPDATE - SET (x, y, is_persistent, updated_at) = - (EXCLUDED.x, EXCLUDED.y, EXCLUDED.is_persistent, now()) - RETURNING *", - form_id, - x, - y, - is_persistent - ) - .fetch_one(pool) - .await - .context("Failed to insert cell") -} - -async fn insert_plugin_cell( - pool: &sqlx::Pool, - plugin_id: i32, - cell_id: i32, - editor_id: Option, -) -> Result { - sqlx::query_as!( - PluginCell, - "INSERT INTO plugin_cells - (plugin_id, cell_id, editor_id, created_at, updated_at) - VALUES ($1, $2, $3, now(), now()) - ON CONFLICT (plugin_id, cell_id) DO UPDATE - SET (editor_id, updated_at) = (EXCLUDED.editor_id, now()) - RETURNING *", - plugin_id, - cell_id, - editor_id, - ) - .fetch_one(pool) - .await - .context("Failed to insert cell") -} - fn rate_limit_wait_duration(res: &Response) -> Result> { let daily_remaining = res .headers() diff --git a/src/models/cell.rs b/src/models/cell.rs new file mode 100644 index 0000000..f5d526e --- /dev/null +++ b/src/models/cell.rs @@ -0,0 +1,40 @@ +use anyhow::{Context, Result}; +use chrono::NaiveDateTime; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Cell { + pub id: i32, + pub form_id: i32, + pub x: Option, + pub y: Option, + pub is_persistent: bool, + pub updated_at: NaiveDateTime, + pub created_at: NaiveDateTime, +} + +pub async fn insert_cell( + pool: &sqlx::Pool, + form_id: i32, + x: Option, + y: Option, + is_persistent: bool, +) -> Result { + sqlx::query_as!( + Cell, + "INSERT INTO cells + (form_id, x, y, is_persistent, created_at, updated_at) + VALUES ($1, $2, $3, $4, now(), now()) + ON CONFLICT (form_id) DO UPDATE + SET (x, y, is_persistent, updated_at) = + (EXCLUDED.x, EXCLUDED.y, EXCLUDED.is_persistent, now()) + RETURNING *", + form_id, + x, + y, + is_persistent + ) + .fetch_one(pool) + .await + .context("Failed to insert cell") +} diff --git a/src/models/file.rs b/src/models/file.rs new file mode 100644 index 0000000..863a46a --- /dev/null +++ b/src/models/file.rs @@ -0,0 +1,52 @@ +use anyhow::{Context, Result}; +use chrono::NaiveDateTime; +use serde::{Deserialize, Serialize}; + +#[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 uploaded_at: NaiveDateTime, + pub updated_at: NaiveDateTime, + pub created_at: NaiveDateTime, +} + +pub async fn insert_file( + pool: &sqlx::Pool, + name: &str, + file_name: &str, + nexus_file_id: i32, + mod_id: i32, + category: Option<&str>, + version: Option<&str>, + mod_version: Option<&str>, + uploaded_at: NaiveDateTime, +) -> Result { + sqlx::query_as!( + File, + "INSERT INTO files + (name, file_name, nexus_file_id, mod_id, category, version, mod_version, uploaded_at, created_at, updated_at) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 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 *", + name, + file_name, + nexus_file_id, + mod_id, + category, + version, + mod_version, + uploaded_at + ) + .fetch_one(pool) + .await + .context("Failed to insert file") +} diff --git a/src/models/game.rs b/src/models/game.rs new file mode 100644 index 0000000..b882ce9 --- /dev/null +++ b/src/models/game.rs @@ -0,0 +1,32 @@ +use anyhow::{Context, Result}; +use chrono::NaiveDateTime; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Game { + pub id: i32, + pub name: String, + pub nexus_game_id: i32, + pub updated_at: NaiveDateTime, + pub created_at: NaiveDateTime, +} + +pub async fn insert_game( + pool: &sqlx::Pool, + name: &str, + nexus_game_id: i32, +) -> Result { + sqlx::query_as!( + Game, + "INSERT INTO games + (name, nexus_game_id, created_at, updated_at) + VALUES ($1, $2, now(), now()) + ON CONFLICT (nexus_game_id, name) DO UPDATE SET (name, updated_at) = (EXCLUDED.name, now()) + RETURNING *", + name, + nexus_game_id + ) + .fetch_one(pool) + .await + .context("Failed to insert game") +} diff --git a/src/models/game_mod.rs b/src/models/game_mod.rs new file mode 100644 index 0000000..18334ed --- /dev/null +++ b/src/models/game_mod.rs @@ -0,0 +1,60 @@ +use anyhow::{Context, Result}; +use chrono::NaiveDateTime; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Mod { + pub id: i32, + pub name: String, + pub nexus_mod_id: i32, + pub author: String, + pub category: String, + pub description: Option, + pub game_id: i32, + pub updated_at: NaiveDateTime, + pub created_at: NaiveDateTime, +} + +pub async fn get_mod_by_nexus_mod_id( + pool: &sqlx::Pool, + nexus_mod_id: i32, +) -> Result> { + sqlx::query_as!( + Mod, + "SELECT * FROM mods WHERE nexus_mod_id = $1", + nexus_mod_id, + ) + .fetch_optional(pool) + .await + .context("Failed to get mod") +} + +pub async fn insert_mod( + pool: &sqlx::Pool, + name: &str, + nexus_mod_id: i32, + author: &str, + category: &str, + description: Option<&str>, + game_id: i32, +) -> Result { + sqlx::query_as!( + Mod, + "INSERT INTO mods + (name, nexus_mod_id, author, category, description, game_id, created_at, updated_at) + VALUES ($1, $2, $3, $4, $5, $6, now(), now()) + ON CONFLICT (game_id, nexus_mod_id) DO UPDATE + SET (name, author, category, description, updated_at) = + (EXCLUDED.name, EXCLUDED.author, EXCLUDED.category, EXCLUDED.description, now()) + RETURNING *", + name, + nexus_mod_id, + author, + category, + description, + game_id + ) + .fetch_one(pool) + .await + .context("Failed to insert or update mod") +} diff --git a/src/models/mod.rs b/src/models/mod.rs new file mode 100644 index 0000000..19b2490 --- /dev/null +++ b/src/models/mod.rs @@ -0,0 +1,6 @@ +pub mod cell; +pub mod file; +pub mod game; +pub mod game_mod; +pub mod plugin; +pub mod plugin_cell; diff --git a/src/models/plugin.rs b/src/models/plugin.rs new file mode 100644 index 0000000..aaf3740 --- /dev/null +++ b/src/models/plugin.rs @@ -0,0 +1,49 @@ +use anyhow::{Context, Result}; +use chrono::NaiveDateTime; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Plugin { + pub id: i32, + pub name: String, + pub hash: i64, + pub file_id: i32, + pub version: Option, + pub author: Option, + pub description: Option, + pub masters: Option>, + pub updated_at: NaiveDateTime, + pub created_at: NaiveDateTime, +} + +pub async fn insert_plugin( + pool: &sqlx::Pool, + name: &str, + hash: i64, + file_id: i32, + version: Option, + author: Option<&str>, + description: Option<&str>, + masters: Option<&[String]>, +) -> Result { + sqlx::query_as!( + Plugin, + "INSERT INTO plugins + (name, hash, file_id, version, author, description, masters, created_at, updated_at) + VALUES ($1, $2, $3, $4, $5, $6, $7, now(), now()) + ON CONFLICT (file_id, name) DO UPDATE + SET (hash, version, author, description, masters, updated_at) = + (EXCLUDED.hash, EXCLUDED.version, EXCLUDED.author, EXCLUDED.description, EXCLUDED.masters, now()) + RETURNING *", + name, + hash, + file_id, + version, + author, + description, + masters + ) + .fetch_one(pool) + .await + .context("Failed to insert plugin") +} \ No newline at end of file diff --git a/src/models/plugin_cell.rs b/src/models/plugin_cell.rs new file mode 100644 index 0000000..46726bc --- /dev/null +++ b/src/models/plugin_cell.rs @@ -0,0 +1,36 @@ +use anyhow::{Context, Result}; +use chrono::NaiveDateTime; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct PluginCell { + pub id: i32, + pub plugin_id: i32, + pub cell_id: i32, + pub editor_id: Option, + pub updated_at: NaiveDateTime, + pub created_at: NaiveDateTime, +} + +pub async fn insert_plugin_cell( + pool: &sqlx::Pool, + plugin_id: i32, + cell_id: i32, + editor_id: Option, +) -> Result { + sqlx::query_as!( + PluginCell, + "INSERT INTO plugin_cells + (plugin_id, cell_id, editor_id, created_at, updated_at) + VALUES ($1, $2, $3, now(), now()) + ON CONFLICT (plugin_id, cell_id) DO UPDATE + SET (editor_id, updated_at) = (EXCLUDED.editor_id, now()) + RETURNING *", + plugin_id, + cell_id, + editor_id, + ) + .fetch_one(pool) + .await + .context("Failed to insert cell") +} \ No newline at end of file