diff --git a/src/endpoints/mods.rs b/src/endpoints/mods.rs index 75e3ee3..b3d2910 100644 --- a/src/endpoints/mods.rs +++ b/src/endpoints/mods.rs @@ -15,7 +15,9 @@ pub struct IndexQueryParams { pub per_page: Option, pub query: Option, #[serde(default)] - pub gd: Option + pub gd: Option, + #[serde(default)] + pub platforms: Option } #[derive(Deserialize)] diff --git a/src/types/models/mod_entity.rs b/src/types/models/mod_entity.rs index 04f6c4f..ae57446 100644 --- a/src/types/models/mod_entity.rs +++ b/src/types/models/mod_entity.rs @@ -1,10 +1,10 @@ use actix_web::web::Bytes; use serde::Serialize; use sqlx::{PgConnection, QueryBuilder, Postgres}; -use std::io::Cursor; +use std::{io::Cursor, str::FromStr}; use crate::{types::{models::mod_version::ModVersion, api::{PaginatedData, ApiError}, mod_json::ModJson}, endpoints::mods::IndexQueryParams}; -use super::mod_gd_version::{ModGDVersion, DetailedGDVersion}; +use super::mod_gd_version::{DetailedGDVersion, ModGDVersion, VerPlatform}; #[derive(Serialize, Debug, sqlx::FromRow)] pub struct Mod { @@ -49,6 +49,14 @@ impl Mod { let limit = per_page; let offset = (page - 1) * per_page; let query_string = format!("%{}%", query.query.unwrap_or("".to_string())); + let mut platforms: Vec = vec![]; + if query.platforms.is_some() { + for i in query.platforms.unwrap().split(",") { + let trimmed = i.trim(); + let platform = VerPlatform::from_str(trimmed).or(Err(ApiError::BadRequest(format!("Invalid platform {}", trimmed))))?; + platforms.push(platform) + } + } let mut builder: QueryBuilder = QueryBuilder::new( "SELECT DISTINCT m.id, m.repository, m.latest_version, m.validated FROM mods m INNER JOIN mod_versions mv ON m.id = mv.mod_id @@ -72,6 +80,21 @@ impl Mod { }, None => () }; + for (i, platform) in platforms.iter().enumerate() { + if i == 0 { + builder.push(" AND mgv.platform IN ("); + counter_builder.push(" AND mgv.platform IN ("); + } + builder.push_bind(platform.clone()); + counter_builder.push_bind(platform.clone()); + if i == platforms.len() - 1 { + builder.push(")"); + counter_builder.push(")"); + } else { + builder.push(", "); + counter_builder.push(", "); + } + } builder.push(" LIMIT "); builder.push_bind(limit); builder.push(" OFFSET "); @@ -104,7 +127,7 @@ impl Mod { } let ids: Vec<_> = records.iter().map(|x| x.id.as_str()).collect(); - let versions = ModVersion::get_latest_for_mods(pool, &ids, query.gd).await?; + let versions = ModVersion::get_latest_for_mods(pool, &ids, query.gd, platforms).await?; let mut mod_version_ids: Vec = vec![]; for i in &versions { let mut version_ids: Vec<_> = i.1.iter().map(|x| { x.id }).collect(); diff --git a/src/types/models/mod_gd_version.rs b/src/types/models/mod_gd_version.rs index 8b75761..e5905a6 100644 --- a/src/types/models/mod_gd_version.rs +++ b/src/types/models/mod_gd_version.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, hash_map::Entry}; +use std::{collections::{HashMap, hash_map::Entry}, str::FromStr}; use serde::{Deserialize, Serialize}; use sqlx::{PgConnection, QueryBuilder, Postgres}; @@ -32,7 +32,24 @@ pub enum VerPlatform { Android, Ios, Mac, - Win + Win, +} + +impl FromStr for VerPlatform { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "android" => Ok(VerPlatform::Android), + "android32" => Ok(VerPlatform::Android), + "android64" => Ok(VerPlatform::Android), + "ios" => Ok(VerPlatform::Ios), + "mac" => Ok(VerPlatform::Mac), + "win" => Ok(VerPlatform::Win), + "windows" => Ok(VerPlatform::Win), + "macos" => Ok(VerPlatform::Mac), + _ => Err(()) + } + } } #[derive(sqlx::FromRow, Clone, Copy, Debug, Serialize)] diff --git a/src/types/models/mod_version.rs b/src/types/models/mod_version.rs index 2dee33a..edc80d5 100644 --- a/src/types/models/mod_version.rs +++ b/src/types/models/mod_version.rs @@ -5,7 +5,7 @@ use sqlx::{PgConnection, QueryBuilder, Postgres, Row}; use crate::types::{api::ApiError, mod_json::{ModJson, ModJsonGDVersionType}}; -use super::{mod_gd_version::{ModGDVersion, GDVersionEnum, DetailedGDVersion}, dependency::{Dependency, ResponseDependency}, incompatibility::{Incompatibility, ResponseIncompatibility}}; +use super::{mod_gd_version::{DetailedGDVersion, GDVersionEnum, ModGDVersion, VerPlatform}, dependency::{Dependency, ResponseDependency}, incompatibility::{Incompatibility, ResponseIncompatibility}}; #[derive(Serialize, Debug, sqlx::FromRow, Clone)] pub struct ModVersion { @@ -71,7 +71,7 @@ impl ModVersion { pub fn modify_download_link(&mut self, app_url: &str) { self.download_link = format!("{}/v1/mods/{}/versions/{}/download", app_url, self.mod_id, self.version); } - pub async fn get_latest_for_mods(pool: &mut PgConnection, ids: &[&str], gd: Option) -> Result>, ApiError> { + pub async fn get_latest_for_mods(pool: &mut PgConnection, ids: &[&str], gd: Option, platforms: Vec) -> Result>, ApiError> { if ids.is_empty() { return Ok(Default::default()); } @@ -88,6 +88,17 @@ impl ModVersion { query_builder.push(" AND mgv.gd = "); query_builder.push_bind(gd.unwrap() as GDVersionEnum); } + for (i, platform) in platforms.iter().enumerate() { + if i == 0 { + query_builder.push(" AND mgv.platform IN ("); + } + query_builder.push_bind(platform.clone()); + if i == platforms.len() - 1 { + query_builder.push(")"); + } else { + query_builder.push(", "); + } + } query_builder.push(" AND mv.mod_id IN ("); let mut separated = query_builder.separated(","); for id in ids.iter() {