From e7c6fae7fe898296a51b9500032b0bde3320f317 Mon Sep 17 00:00:00 2001 From: nixxoq Date: Mon, 26 May 2025 21:41:29 +0300 Subject: [PATCH 01/12] feature(crate): implement Versions --- src/api/mod.rs | 1 + src/api/versions.rs | 153 ++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/structs/mod.rs | 1 + src/structs/versions.rs | 98 +++++++++++++++++++++++++ src/utils.rs | 25 +++++++ 6 files changed, 279 insertions(+) create mode 100644 src/api/versions.rs create mode 100644 src/structs/versions.rs diff --git a/src/api/mod.rs b/src/api/mod.rs index 330da9e..acddf66 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -1,4 +1,5 @@ pub mod search; pub mod projects; +pub mod versions; use crate::{ModrinthAPI, Result, BASE_URL}; diff --git a/src/api/versions.rs b/src/api/versions.rs new file mode 100644 index 0000000..de82b42 --- /dev/null +++ b/src/api/versions.rs @@ -0,0 +1,153 @@ +//! API functions related to files (versions) + +use super::*; +use crate::{ + structs::versions::*, + utils::{RequestBuilderCustomSend, UrlJoinAll, UrlWithQuery, check_id_slug}, +}; + +impl ModrinthAPI { + /// Retrieves a list of project versions, optionally filtered, or a single specific version. + /// + /// # Arguments + /// * `project_id` - Project slug/id (`&str`). + /// * `extra_options` - `Option>`: Optional parameters to filter the list of versions + /// or to retrieve a specific version. + /// + /// If `extra_options` is `None`, all versions for the project will be returned without any filters. + /// + /// Fields of `ExtraOptions`: + /// - `number`: `Option<&str>` - If `Some`, this should be the specific version ID or a version number string (e.g., "1.0.0"). + /// When `number` is provided, the function attempts to fetch that specific version. In this case, + /// the other filtering parameters (`loaders`, `game_versions`, `featured`) within `ExtraOptions` + /// will be ignored by the Modrinth API, as the request targets a single version endpoint + /// (`/project/{id}/version/{number}`). The returned `Vec` will contain at most one element. + /// Example: `number: Some("0.76.0+1.19.2")` + /// + /// - `loaders`: `Option<&[&str]>` - A slice of loader IDs (e.g., `&["forge", "fabric"]`) + /// to filter the list of versions. This is only applied if `number` is `None`. + /// Example: `loaders: Some(&["fabric", "quilt"])` + /// + /// - `game_versions`: `Option<&[&str]>` - A slice of game version IDs (e.g., `&["1.19.2", "1.20.1"]`) + /// to filter the list of versions. This is only applied if `number` is `None`. + /// Example: `game_versions: Some(&["1.20.1"])` + /// + /// - `featured`: `Option` - If `Some(true)`, only featured versions will be returned. + /// If `Some(false)`, featured versions will be excluded. If `None`, both featured and + /// non-featured versions are included. This is only applied if `number` is `None`. + /// Example: `featured: Some(true)` + /// + /// Example usage of `extra_options`: + /// ```no_run + /// # use modrinth_api::structs::versions::ExtraOptions; + /// let options_for_filtered_list = ExtraOptions { + /// loaders: Some(&["fabric"]), + /// game_versions: Some(&["1.20.1"]), + /// featured: Some(true), + /// }; + /// ``` + /// + /// # Returns + /// `Result>`: + /// - `Ok(Vec)`: A list of the [`Version`] structs. If `number` was specified in `extra_options`, + /// this vector will contain at most one [`Version`] struct. + /// - `Err(crate::error::Error)`: An error occurred during the API request or data processing. + pub async fn get_project_versions( + &self, + project_id: &str, + extra_options: Option>, + ) -> Result> { + check_id_slug(&[project_id])?; + + let mut url = BASE_URL.join_all(vec!["project", project_id, "version"]); + + match extra_options { + Some(extra_options) => { + url = BASE_URL.join_all(vec![ + "project", project_id, "version", + // extra_options.number.unwrap_or(""), + ]); + url = url.add_optional_query_json("loaders", extra_options.loaders)?; + url = url.add_optional_query_json("game_versions", extra_options.game_versions)?; + url = url.add_optional_query_json("featured", extra_options.featured)?; + } + None => {} + } + + self.client.get(url).custom_send_json().await + } + + /// Get the version from the version id + /// + /// # Arguments + /// * `version_id` - The ID of the version (`&str`) + /// + /// # Returns + /// + /// `Result`: + /// - `Ok(Version)`: [`Version`] struct. + /// - `Err(crate::error::Error)`: An error occurred during the API request or data processing. + pub async fn get_version(&self, version_id: &str) -> Result { + check_id_slug(&[version_id])?; + self.client + .get(BASE_URL.join_all(vec!["version", version_id])) + .custom_send_json() + .await + } + + // + pub async fn version_list_filtered( + &self, + project_id: &str, + loaders: Option<&[&str]>, + game_versions: Option<&[&str]>, + featured: Option, + ) -> Result> { + check_id_slug(&[project_id])?; + let mut url = BASE_URL.join_all(vec!["project", project_id, "version"]); + if let Some(loaders) = loaders { + url = url.with_query_json("loaders", loaders)?; + } + if let Some(game_versions) = game_versions { + url = url.with_query_json("game_versions", game_versions)?; + } + if let Some(featured) = featured { + url = url.with_query_json("featured", featured)?; + } + + println!("{url}"); + self.client.get(url).custom_send_json().await + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn get_version_from_project() -> Result<()> { + let api = ModrinthAPI::default(); + let res = api.get_project_versions("AANobbMI", None).await?; + + let result = res.first(); + assert!(result.is_some()); + assert!(result.unwrap().name.contains("Sodium")); // assume that is Sodium... + Ok(()) + } + + #[tokio::test] + async fn get_version_from_project_extra() -> Result<()> { + let api = ModrinthAPI::default(); + let options = ExtraOptions { + loaders: Some(&["fabric"]), + game_versions: Some(&["1.20.1"]), + featured: Some(true), + }; + let res = api.get_project_versions("AANobbMI", Some(options)).await?; + + let result = res.first(); + assert!(result.is_some()); + assert!(result.unwrap().name.contains("Sodium")); // assume that is Sodium... + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index f333732..7ab30c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,7 @@ pub enum Error { ReqwestError(#[from] reqwest::Error), JSONError(#[from] serde_json::Error), InvalidHeaderValue(#[from] InvalidHeaderValue), + ParseError(#[from] url::ParseError), } pub type Result = std::result::Result; diff --git a/src/structs/mod.rs b/src/structs/mod.rs index e5f9545..0e4dfb5 100644 --- a/src/structs/mod.rs +++ b/src/structs/mod.rs @@ -1,5 +1,6 @@ pub mod projects; pub mod search; +pub mod versions; use crate::{ModrinthAPI, Result, structs::projects::Project}; use serde::{Deserialize, Serialize}; diff --git a/src/structs/versions.rs b/src/structs/versions.rs new file mode 100644 index 0000000..85cc5d4 --- /dev/null +++ b/src/structs/versions.rs @@ -0,0 +1,98 @@ +use super::*; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct Version { + pub name: String, + pub version_number: String, + pub changelog: Option, + pub dependencies: Vec, + pub game_versions: Vec, + pub version_type: VersionType, + pub loaders: Vec, + pub featured: bool, + pub status: Option, + pub requested_status: Option, + pub id: String, + pub project_id: String, + pub author_id: String, + pub date_published: Date, + pub downloads: usize, + pub files: Vec, +} + +#[derive(Debug)] +pub struct ExtraOptions<'a> { + // pub number: Option<&'a str>, + pub loaders: Option<&'a [&'a str]>, + pub game_versions: Option<&'a [&'a str]>, + pub featured: Option, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct Dependencies { + pub version_id: Option, + pub project_id: Option, + pub file_name: Option, + pub dependency_type: DependencyType, +} + +#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum DependencyType { + Required, + Optional, + Incompatible, + Embedded, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum VersionType { + Release, + Beta, + Alpha, +} + +#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)] +#[serde(rename_all = "lowercase")] +pub enum Status { + Listed, + Archived, + Draft, + Unlisted, + Scheduled, + Unknown, +} + +#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)] +#[serde(rename_all = "lowercase")] +pub enum RequestedStatus { + Listed, + Archived, + Draft, + Unlisted, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct File { + pub hashes: Hash, + pub url: String, + pub filename: String, + pub primary: bool, + pub size: usize, + pub file_type: Option, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct Hash { + pub sha512: String, + pub sha1: String, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(rename_all = "kebab-case")] +pub enum FileType { + RequiredResourcePack, + OptionalResourcePack, +} diff --git a/src/utils.rs b/src/utils.rs index 8cc196e..0a3031a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -88,6 +88,20 @@ pub trait UrlWithQuery: Sized { name: impl AsRef, value: impl Serialize, ) -> Self::SerialiseResult; + + /// Adds a query parameter to the URL only if the `value` is `Some(T)`. + /// The `value` is serialized to JSON. + /// + /// # Arguments + /// * `key` - The name of the query parameter. + /// * `value` - An `Option` containing the value to be serialized. If `None`, no parameter is added. + /// + /// # Returns + fn add_optional_query_json( + self, + key: impl AsRef, + value: Option, + ) -> Result; } impl UrlWithQuery for Url { @@ -108,4 +122,15 @@ impl UrlWithQuery for Url { .append_pair(name.as_ref(), &serde_json::to_string(&value)?); Ok(self) } + + fn add_optional_query_json( + self, + key: impl AsRef, // Must match trait signature + value: Option, + ) -> Result { + match value { + Some(value) => Ok(self.with_query_json(key, value)?), + None => Ok(self), + } + } } From 47ce84e7146ee0b2bdf99f0ae19ca5cbbe36bddb Mon Sep 17 00:00:00 2001 From: nixxoq Date: Mon, 26 May 2025 21:44:56 +0300 Subject: [PATCH 02/12] chore(api/versions): remove `number` field from `get_project_versions` function (will be moved to the `get_project_version` function) --- src/api/versions.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/api/versions.rs b/src/api/versions.rs index de82b42..4073e05 100644 --- a/src/api/versions.rs +++ b/src/api/versions.rs @@ -17,12 +17,6 @@ impl ModrinthAPI { /// If `extra_options` is `None`, all versions for the project will be returned without any filters. /// /// Fields of `ExtraOptions`: - /// - `number`: `Option<&str>` - If `Some`, this should be the specific version ID or a version number string (e.g., "1.0.0"). - /// When `number` is provided, the function attempts to fetch that specific version. In this case, - /// the other filtering parameters (`loaders`, `game_versions`, `featured`) within `ExtraOptions` - /// will be ignored by the Modrinth API, as the request targets a single version endpoint - /// (`/project/{id}/version/{number}`). The returned `Vec` will contain at most one element. - /// Example: `number: Some("0.76.0+1.19.2")` /// /// - `loaders`: `Option<&[&str]>` - A slice of loader IDs (e.g., `&["forge", "fabric"]`) /// to filter the list of versions. This is only applied if `number` is `None`. From e8300927c70a9855d7a55a485b0361f5a3ffd7fc Mon Sep 17 00:00:00 2001 From: nixxoq Date: Thu, 29 May 2025 17:48:03 +0300 Subject: [PATCH 03/12] feat(structs/versions): separate `ExtraOptions` to ProjectVersionsFilter (for get_project_versions) and ProjectVersionParam (for get_project_version function) feat(structs/versions): move information about fields directly to structures --- src/structs/versions.rs | 74 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/src/structs/versions.rs b/src/structs/versions.rs index 85cc5d4..86c55c9 100644 --- a/src/structs/versions.rs +++ b/src/structs/versions.rs @@ -1,6 +1,9 @@ use super::*; use serde::{Deserialize, Serialize}; +/// The Version struct +/// +/// Documentation: https://docs.modrinth.com/api/operations/getprojectversions/#200 #[derive(Serialize, Deserialize, Debug)] pub struct Version { pub name: String, @@ -21,14 +24,81 @@ pub struct Version { pub files: Vec, } +/// Extra parameters for [ModrinthAPI::get_project_versions] function +/// +/// Fields of `ProjectVersionsFilter`: +/// +/// - `loaders`: `Option<&[&str]>` - A slice of loader IDs (e.g., `&["forge", "fabric"]`) +/// to filter the list of versions. This is only applied if `number` is `None`. +/// Example: `loaders: Some(&["fabric", "quilt"])` +/// +/// - `game_versions`: `Option<&[&str]>` - A slice of game version IDs (e.g., `&["1.19.2", "1.20.1"]`) +/// to filter the list of versions. This is only applied if `number` is `None`. +/// Example: `game_versions: Some(&["1.20.1"])` +/// +/// - `featured`: `Option` - If `Some(true)`, only featured versions will be returned. +/// If `Some(false)`, featured versions will be excluded. If `None`, both featured and +/// non-featured versions are included. This is only applied if `number` is `None`. +/// Example: `featured: Some(true)` #[derive(Debug)] -pub struct ExtraOptions<'a> { - // pub number: Option<&'a str>, +pub struct ProjectVersionsFilter<'a> { pub loaders: Option<&'a [&'a str]>, pub game_versions: Option<&'a [&'a str]>, pub featured: Option, } +impl<'a> Default for ProjectVersionsFilter<'a> { + fn default() -> Self { + ProjectVersionsFilter { + loaders: None, + game_versions: None, + featured: None, + } + } +} + +/// Extra parameters for [ModrinthAPI::get_project_version] function +/// +/// Fields +/// +/// - `number`: `Option<&str>` - Specific version ID or a version number string (e.g., "1.0.0"). +/// Example: `number: Some("0.76.0+1.19.2")` +/// +/// - `loaders`: `Option<&[&str]>` - A slice of loader IDs (e.g., `&["forge", "fabric"]`) +/// to filter the list of versions. This is only applied if `number` is `None`. +/// Example: `loaders: Some(&["fabric", "quilt"])` +/// +/// - `game_versions`: `Option<&[&str]>` - A slice of game version IDs (e.g., `&["1.19.2", "1.20.1"]`) +/// to filter the list of versions. This is only applied if `number` is `None`. +/// Example: `game_versions: Some(&["1.20.1"])` +/// +/// - `featured`: `Option` - If `Some(true)`, only featured versions will be returned. +/// If `Some(false)`, featured versions will be excluded. If `None`, both featured and +/// non-featured versions are included. This is only applied if `number` is `None`. +/// Example: `featured: Some(true)` +#[derive(Debug)] +pub struct ProjectVersionParams<'a> { + /// Get a version given a version number or ID + /// + /// Note: + /// * if the version number provided matches multiple versions, only the oldest matching version will be returned. + pub number: Option<&'a str>, + pub loaders: Option<&'a [&'a str]>, + pub game_versions: Option<&'a [&'a str]>, + pub featured: Option, +} + +impl<'a> Default for ProjectVersionParams<'a> { + fn default() -> Self { + ProjectVersionParams { + number: Some(""), + loaders: None, + game_versions: None, + featured: None, + } + } +} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct Dependencies { pub version_id: Option, From 8b0503e85654cbbe561a4e8d488a13ee83712779 Mon Sep 17 00:00:00 2001 From: nixxoq Date: Thu, 29 May 2025 17:49:53 +0300 Subject: [PATCH 04/12] feat(versions): implement `get_project_version` function (Retrieves a project version with custom filtering) feat(versions): add tests related with get_project_version func chore(versions): docstring changes --- src/api/versions.rs | 119 ++++++++++++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 37 deletions(-) diff --git a/src/api/versions.rs b/src/api/versions.rs index 4073e05..fa63382 100644 --- a/src/api/versions.rs +++ b/src/api/versions.rs @@ -7,34 +7,20 @@ use crate::{ }; impl ModrinthAPI { - /// Retrieves a list of project versions, optionally filtered, or a single specific version. + /// Retrieves a list of project versions with custom filtering. /// /// # Arguments /// * `project_id` - Project slug/id (`&str`). - /// * `extra_options` - `Option>`: Optional parameters to filter the list of versions + /// * `extra_options` - `Option>` ([`ProjectVersionsFilter`]): Optional parameters to filter the list of versions /// or to retrieve a specific version. /// /// If `extra_options` is `None`, all versions for the project will be returned without any filters. /// - /// Fields of `ExtraOptions`: - /// - /// - `loaders`: `Option<&[&str]>` - A slice of loader IDs (e.g., `&["forge", "fabric"]`) - /// to filter the list of versions. This is only applied if `number` is `None`. - /// Example: `loaders: Some(&["fabric", "quilt"])` - /// - /// - `game_versions`: `Option<&[&str]>` - A slice of game version IDs (e.g., `&["1.19.2", "1.20.1"]`) - /// to filter the list of versions. This is only applied if `number` is `None`. - /// Example: `game_versions: Some(&["1.20.1"])` - /// - /// - `featured`: `Option` - If `Some(true)`, only featured versions will be returned. - /// If `Some(false)`, featured versions will be excluded. If `None`, both featured and - /// non-featured versions are included. This is only applied if `number` is `None`. - /// Example: `featured: Some(true)` - /// /// Example usage of `extra_options`: /// ```no_run - /// # use modrinth_api::structs::versions::ExtraOptions; - /// let options_for_filtered_list = ExtraOptions { + /// use modrinth_api::structs::versions::ProjectVersionsFilter; + /// + /// let options_for_filtered_list = ProjectVersionsFilter { /// loaders: Some(&["fabric"]), /// game_versions: Some(&["1.20.1"]), /// featured: Some(true), @@ -49,7 +35,7 @@ impl ModrinthAPI { pub async fn get_project_versions( &self, project_id: &str, - extra_options: Option>, + extra_options: Option>, ) -> Result> { check_id_slug(&[project_id])?; @@ -81,7 +67,7 @@ impl ModrinthAPI { /// `Result`: /// - `Ok(Version)`: [`Version`] struct. /// - `Err(crate::error::Error)`: An error occurred during the API request or data processing. - pub async fn get_version(&self, version_id: &str) -> Result { + pub async fn get_version_by_id(&self, version_id: &str) -> Result { check_id_slug(&[version_id])?; self.client .get(BASE_URL.join_all(vec!["version", version_id])) @@ -89,27 +75,56 @@ impl ModrinthAPI { .await } - // - pub async fn version_list_filtered( + /// Retrieves a project [`Version`] with custom filtering. + /// + /// # Arguments + /// * `project_id` - Project slug/id (`&str`). + /// * `extra_options` - `Option>`: Optional parameters to filter the list of versions + /// or to retrieve a specific version. + /// + /// If `extra_options` is `None`, all versions for the project will be returned without any filters. + /// + /// Example usage of `extra_options`: + /// ```no_run + /// use modrinth_api::structs::versions::ProjectVersionParams; + /// + /// let options_for_filtered_list = ProjectVersionParams { + /// number: Some("mc1.20.1-0.5.13-fabric"), // or Some("OihdIimA") + /// loaders: Some(&["fabric"]), + /// game_versions: Some(&["1.20.1"]), + /// featured: Some(true), + /// }; + /// ``` + /// + /// # Returns + /// `Result>`: + /// - `Ok(Vec)`: A list of the [`Version`] structs. If `number` was specified in `extra_options`, + /// this vector will contain at most one [`Version`] struct. + /// - `Err(crate::error::Error)`: An error occurred during the API request or data processing. + pub async fn get_project_version( &self, project_id: &str, - loaders: Option<&[&str]>, - game_versions: Option<&[&str]>, - featured: Option, - ) -> Result> { + extra_options: Option>, + ) -> Result { check_id_slug(&[project_id])?; + let mut url = BASE_URL.join_all(vec!["project", project_id, "version"]); - if let Some(loaders) = loaders { - url = url.with_query_json("loaders", loaders)?; - } - if let Some(game_versions) = game_versions { - url = url.with_query_json("game_versions", game_versions)?; - } - if let Some(featured) = featured { - url = url.with_query_json("featured", featured)?; + + match extra_options { + Some(extra_options) => { + url = BASE_URL.join_all(vec![ + "project", + project_id, + "version", + extra_options.number.unwrap(), + ]); + url = url.add_optional_query_json("loaders", extra_options.loaders)?; + url = url.add_optional_query_json("game_versions", extra_options.game_versions)?; + url = url.add_optional_query_json("featured", extra_options.featured)?; + } + None => {} } - println!("{url}"); self.client.get(url).custom_send_json().await } } @@ -132,7 +147,7 @@ mod tests { #[tokio::test] async fn get_version_from_project_extra() -> Result<()> { let api = ModrinthAPI::default(); - let options = ExtraOptions { + let options = ProjectVersionsFilter { loaders: Some(&["fabric"]), game_versions: Some(&["1.20.1"]), featured: Some(true), @@ -144,4 +159,34 @@ mod tests { assert!(result.unwrap().name.contains("Sodium")); // assume that is Sodium... Ok(()) } + + #[tokio::test] + async fn get_single_version_from_project_extra() -> Result<()> { + let api = ModrinthAPI::default(); + let options = ProjectVersionParams { + number: Some("mc1.20.1-0.5.13-fabric"), + loaders: Some(&["fabric"]), + game_versions: Some(&["1.20.1"]), + featured: Some(true), + }; + let result = api.get_project_version("AANobbMI", Some(options)).await?; + + assert!(result.name.contains("Sodium")); // assume that is Sodium... + Ok(()) + } + + #[tokio::test] + async fn get_single_version_from_project_with_wrong_id() -> Result<()> { + let api = ModrinthAPI::default(); + let options = ProjectVersionParams { + number: Some("2"), + loaders: Some(&["fabric"]), + game_versions: Some(&["1.20.1"]), + featured: Some(true), + }; + let result = api.get_project_version("AANobbMI", Some(options)).await; + + assert!(result.is_err()); + Ok(()) + } } From bb1408488281665994a4e64dade8b66fb2636f19 Mon Sep 17 00:00:00 2001 From: nixxoq Date: Thu, 29 May 2025 19:04:33 +0300 Subject: [PATCH 05/12] chore(fmt): fix rustfmt linting --- src/api/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/mod.rs b/src/api/mod.rs index acddf66..0af0f22 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -1,5 +1,5 @@ -pub mod search; pub mod projects; +pub mod search; pub mod versions; -use crate::{ModrinthAPI, Result, BASE_URL}; +use crate::{BASE_URL, ModrinthAPI, Result}; From d2b7993b435f816da62c3c7886f9f2d56d969db9 Mon Sep 17 00:00:00 2001 From: nixxoq Date: Thu, 29 May 2025 19:08:18 +0300 Subject: [PATCH 06/12] chore(clippy): fix clippy warnings --- src/api/versions.rs | 42 ++++++++++++++++++----------------------- src/structs/versions.rs | 11 +---------- 2 files changed, 19 insertions(+), 34 deletions(-) diff --git a/src/api/versions.rs b/src/api/versions.rs index fa63382..02a5bad 100644 --- a/src/api/versions.rs +++ b/src/api/versions.rs @@ -41,17 +41,14 @@ impl ModrinthAPI { let mut url = BASE_URL.join_all(vec!["project", project_id, "version"]); - match extra_options { - Some(extra_options) => { - url = BASE_URL.join_all(vec![ - "project", project_id, "version", - // extra_options.number.unwrap_or(""), - ]); - url = url.add_optional_query_json("loaders", extra_options.loaders)?; - url = url.add_optional_query_json("game_versions", extra_options.game_versions)?; - url = url.add_optional_query_json("featured", extra_options.featured)?; - } - None => {} + if let Some(extra_options) = extra_options { + url = BASE_URL.join_all(vec![ + "project", project_id, "version", + // extra_options.number.unwrap_or(""), + ]); + url = url.add_optional_query_json("loaders", extra_options.loaders)?; + url = url.add_optional_query_json("game_versions", extra_options.game_versions)?; + url = url.add_optional_query_json("featured", extra_options.featured)?; } self.client.get(url).custom_send_json().await @@ -110,19 +107,16 @@ impl ModrinthAPI { let mut url = BASE_URL.join_all(vec!["project", project_id, "version"]); - match extra_options { - Some(extra_options) => { - url = BASE_URL.join_all(vec![ - "project", - project_id, - "version", - extra_options.number.unwrap(), - ]); - url = url.add_optional_query_json("loaders", extra_options.loaders)?; - url = url.add_optional_query_json("game_versions", extra_options.game_versions)?; - url = url.add_optional_query_json("featured", extra_options.featured)?; - } - None => {} + if let Some(extra_options) = extra_options { + url = BASE_URL.join_all(vec![ + "project", + project_id, + "version", + extra_options.number.unwrap(), + ]); + url = url.add_optional_query_json("loaders", extra_options.loaders)?; + url = url.add_optional_query_json("game_versions", extra_options.game_versions)?; + url = url.add_optional_query_json("featured", extra_options.featured)?; } self.client.get(url).custom_send_json().await diff --git a/src/structs/versions.rs b/src/structs/versions.rs index 86c55c9..894dec2 100644 --- a/src/structs/versions.rs +++ b/src/structs/versions.rs @@ -40,22 +40,13 @@ pub struct Version { /// If `Some(false)`, featured versions will be excluded. If `None`, both featured and /// non-featured versions are included. This is only applied if `number` is `None`. /// Example: `featured: Some(true)` -#[derive(Debug)] +#[derive(Debug, Default)] pub struct ProjectVersionsFilter<'a> { pub loaders: Option<&'a [&'a str]>, pub game_versions: Option<&'a [&'a str]>, pub featured: Option, } -impl<'a> Default for ProjectVersionsFilter<'a> { - fn default() -> Self { - ProjectVersionsFilter { - loaders: None, - game_versions: None, - featured: None, - } - } -} /// Extra parameters for [ModrinthAPI::get_project_version] function /// From bcb6f52b2f56a4da243a966e6ce6d324d5b837bd Mon Sep 17 00:00:00 2001 From: nixxoq Date: Thu, 29 May 2025 19:08:18 +0300 Subject: [PATCH 07/12] chore(clippy): fix clippy warnings --- src/structs/versions.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/structs/versions.rs b/src/structs/versions.rs index 894dec2..44629d1 100644 --- a/src/structs/versions.rs +++ b/src/structs/versions.rs @@ -47,7 +47,6 @@ pub struct ProjectVersionsFilter<'a> { pub featured: Option, } - /// Extra parameters for [ModrinthAPI::get_project_version] function /// /// Fields From c5ae0f8920865791ea572625db33cf844e622731 Mon Sep 17 00:00:00 2001 From: nixxoq Date: Thu, 29 May 2025 22:26:22 +0300 Subject: [PATCH 08/12] feat(search): drop old `search` implement; extended_search now references to ModrinthAPI::search function (will be removed later) chore(crate): update examples --- examples/basic_search.rs | 11 ----------- examples/extended_search.rs | 21 --------------------- src/api/search.rs | 31 +++++++++++++++++-------------- 3 files changed, 17 insertions(+), 46 deletions(-) delete mode 100644 examples/basic_search.rs delete mode 100644 examples/extended_search.rs diff --git a/examples/basic_search.rs b/examples/basic_search.rs deleted file mode 100644 index 549235c..0000000 --- a/examples/basic_search.rs +++ /dev/null @@ -1,11 +0,0 @@ -use modrinth_api::structs::search::Sort; -use modrinth_api::{Error, ModrinthAPI}; - -#[tokio::main] -async fn main() -> Result<(), Error> { - let api = ModrinthAPI::default(); - let result = api.search("xaeros", &Sort::Downloads, None).await?; - - println!("{:#?}", result); - Ok(()) -} diff --git a/examples/extended_search.rs b/examples/extended_search.rs deleted file mode 100644 index 9ce0838..0000000 --- a/examples/extended_search.rs +++ /dev/null @@ -1,21 +0,0 @@ -use modrinth_api::structs::search::{ExtendedSearch, Sort}; -use modrinth_api::{Error, ModrinthAPI}; - -#[tokio::main] -async fn main() -> Result<(), Error> { - let api = ModrinthAPI::default(); - let result = api - .extended_search( - "xaeros", - &Sort::Downloads, - Some(20), - Some(ExtendedSearch { - offset: None, // The offset into the search. Skips this number of results - facets: vec![], // Facets are an essential concept for understanding how to filter out results. - }), - ) - .await?; - - println!("{:#?}", result); - Ok(()) -} diff --git a/src/api/search.rs b/src/api/search.rs index 154139e..4003b46 100644 --- a/src/api/search.rs +++ b/src/api/search.rs @@ -64,7 +64,7 @@ impl ModrinthAPI { /// Ok(()) /// } /// ``` - pub async fn extended_search( + pub async fn search( &self, query: &str, sort: &Sort, @@ -92,19 +92,22 @@ impl ModrinthAPI { self.client.get(url).custom_send_json().await } - #[deprecated(since = "0.1.1", note = "Migrate to `extended_search` method")] - pub async fn search(&self, query: &str, sort: &Sort, limit: Option) -> Result { - let limit = limit.unwrap_or(20); - - let url = BASE_URL - .join_all(vec!["search"]) - .with_query("query", query) - .with_query("index", sort) - .with_query("limit", limit); - - println!("{}", url); - - self.client.get(url).custom_send_json().await + /// Performs an extended search for projects on Modrinth, allowing for more granular control over the search + /// results through various filtering options. + /// + /// This function is a reference to [ModrinthAPI::search] function (backward compatibility) + #[deprecated( + since = "0.2.0", + note = "This function is a backward compatibility with older versions of modrinth-api-rs. Please use ModrinthAPI::search instead" + )] + pub async fn extended_search( + &self, + query: &str, + sort: &Sort, + limit: Option, + extended_search: Option, + ) -> Result { + self.search(query, sort, limit, extended_search).await } } From f378ebcd47d8afd16ff082acd04bdecd7fd00f9d Mon Sep 17 00:00:00 2001 From: nixxoq Date: Thu, 29 May 2025 22:29:20 +0300 Subject: [PATCH 09/12] feat(search): tests now uses `ModrinthAPI::search` function instead of `ModrinthAPI::extended_search` --- examples/search.rs | 21 +++++++++++++++++++++ src/api/search.rs | 6 +++--- 2 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 examples/search.rs diff --git a/examples/search.rs b/examples/search.rs new file mode 100644 index 0000000..3c18305 --- /dev/null +++ b/examples/search.rs @@ -0,0 +1,21 @@ +use modrinth_api::structs::search::{ExtendedSearch, Sort}; +use modrinth_api::{Error, ModrinthAPI}; + +#[tokio::main] +async fn main() -> Result<(), Error> { + let api = ModrinthAPI::default(); + let result = api + .extended_search( + "xaeros", // Query + &Sort::Downloads, // Sort + Some(20), // Maximum number of results to return. + Some(ExtendedSearch { + offset: None, // The offset into the search. Skips this number of results + facets: vec![], // Facets are an essential concept for understanding how to filter out results. + }), + ) + .await?; + + println!("{:#?}", result); + Ok(()) +} diff --git a/src/api/search.rs b/src/api/search.rs index 4003b46..b839739 100644 --- a/src/api/search.rs +++ b/src/api/search.rs @@ -120,7 +120,7 @@ mod tests { async fn search_project() -> Result<()> { let api = ModrinthAPI::default(); let response = api - .extended_search( + .search( "xaeros", &Sort::Downloads, None, @@ -143,7 +143,7 @@ mod tests { async fn test_fetching_project_with_mut() -> Result<()> { let api = ModrinthAPI::default(); let response = api - .extended_search( + .search( "xaeros", &Sort::Downloads, None, @@ -170,7 +170,7 @@ mod tests { async fn test_fetching_project_without_mut() -> Result<()> { let api = ModrinthAPI::default(); let response = api - .extended_search( + .search( "xaeros", &Sort::Downloads, None, From 006bdb4fef4467b8703142b47330ce5d228c96e0 Mon Sep 17 00:00:00 2001 From: nixxoq Date: Thu, 29 May 2025 22:31:00 +0300 Subject: [PATCH 10/12] chore(examples): use search function instead of extended_search fuck my own ci... --- examples/search.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/search.rs b/examples/search.rs index 3c18305..40cc02d 100644 --- a/examples/search.rs +++ b/examples/search.rs @@ -5,7 +5,7 @@ use modrinth_api::{Error, ModrinthAPI}; async fn main() -> Result<(), Error> { let api = ModrinthAPI::default(); let result = api - .extended_search( + .search( "xaeros", // Query &Sort::Downloads, // Sort Some(20), // Maximum number of results to return. From a5ff70246ec0c17ae1d297b4a3ac3ff370809d20 Mon Sep 17 00:00:00 2001 From: nixxoq Date: Sat, 31 May 2025 11:37:55 +0300 Subject: [PATCH 11/12] chore(crate): move all tests to their folder --- src/api/projects.rs | 22 ---------- src/api/search.rs | 83 ----------------------------------- src/api/versions.rs | 62 -------------------------- src/structs/versions.rs | 2 +- tests/search_tests.rs | 97 +++++++++++++++++++++++++++++++++++++++++ tests/versions_tests.rs | 59 +++++++++++++++++++++++++ 6 files changed, 157 insertions(+), 168 deletions(-) create mode 100644 tests/search_tests.rs create mode 100644 tests/versions_tests.rs diff --git a/src/api/projects.rs b/src/api/projects.rs index 02230c7..e3b3f30 100644 --- a/src/api/projects.rs +++ b/src/api/projects.rs @@ -26,25 +26,3 @@ impl ModrinthAPI { .await } } - -#[cfg(test)] -mod tests { - use super::*; - - #[tokio::test] - async fn get_valid_project() -> Result<()> { - let api = ModrinthAPI::default(); - // HVnmMxH1 -> Complementary Shaders - Reimagined - let response = api.get_project_by_id("HVnmMxH1").await?; - assert_eq!(response.title, "Complementary Shaders - Reimagined"); - Ok(()) - } - - #[tokio::test] - async fn asrt_slug_error() -> Result<()> { - let api = ModrinthAPI::default(); - let response = api.get_project_by_id("dffdsfdsfsdfdsf").await; - assert!(response.is_err()); - Ok(()) - } -} diff --git a/src/api/search.rs b/src/api/search.rs index b839739..4dadc59 100644 --- a/src/api/search.rs +++ b/src/api/search.rs @@ -110,86 +110,3 @@ impl ModrinthAPI { self.search(query, sort, limit, extended_search).await } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::structs::{projects::ProjectType, search::Facet}; - - #[tokio::test] - async fn search_project() -> Result<()> { - let api = ModrinthAPI::default(); - let response = api - .search( - "xaeros", - &Sort::Downloads, - None, - Some(ExtendedSearch { - offset: None, - facets: vec![vec![Facet::ProjectType(ProjectType::Mod)]], - }), - ) - .await?; - - let response = response.hits.first().unwrap(); - let title = response.slug.as_ref(); - - assert!(title.is_some()); - assert_eq!(title.ok_or(0), Ok(&String::from("xaeros-minimap"))); - Ok(()) - } - - #[tokio::test] - async fn test_fetching_project_with_mut() -> Result<()> { - let api = ModrinthAPI::default(); - let response = api - .search( - "xaeros", - &Sort::Downloads, - None, - Some(ExtendedSearch { - offset: None, - facets: vec![vec![Facet::ProjectType(ProjectType::Mod)]], - }), - ) - .await?; - - let response = response.hits.first().unwrap(); - let title = response.slug.as_ref(); - - assert!(title.is_some()); - assert_eq!(title.ok_or(0), Ok(&String::from("xaeros-minimap"))); - - let hit = response.to_owned().fetch_project(&api).await?; - - assert_eq!(hit.slug.as_ref().unwrap(), &String::from("xaeros-minimap")); - Ok(()) - } - - #[tokio::test] - async fn test_fetching_project_without_mut() -> Result<()> { - let api = ModrinthAPI::default(); - let response = api - .search( - "xaeros", - &Sort::Downloads, - None, - Some(ExtendedSearch { - offset: None, - facets: vec![vec![Facet::ProjectType(ProjectType::Mod)]], - }), - ) - .await?; - - let response = response.hits.first().unwrap(); - let title = response.slug.as_ref(); - - assert!(title.is_some()); - assert_eq!(title.ok_or(0), Ok(&String::from("xaeros-minimap"))); - - let hit = response.get_full_project(&api).await?; - - assert_eq!(hit.slug, String::from("xaeros-minimap")); - Ok(()) - } -} diff --git a/src/api/versions.rs b/src/api/versions.rs index 02a5bad..b787286 100644 --- a/src/api/versions.rs +++ b/src/api/versions.rs @@ -122,65 +122,3 @@ impl ModrinthAPI { self.client.get(url).custom_send_json().await } } - -#[cfg(test)] -mod tests { - use super::*; - - #[tokio::test] - async fn get_version_from_project() -> Result<()> { - let api = ModrinthAPI::default(); - let res = api.get_project_versions("AANobbMI", None).await?; - - let result = res.first(); - assert!(result.is_some()); - assert!(result.unwrap().name.contains("Sodium")); // assume that is Sodium... - Ok(()) - } - - #[tokio::test] - async fn get_version_from_project_extra() -> Result<()> { - let api = ModrinthAPI::default(); - let options = ProjectVersionsFilter { - loaders: Some(&["fabric"]), - game_versions: Some(&["1.20.1"]), - featured: Some(true), - }; - let res = api.get_project_versions("AANobbMI", Some(options)).await?; - - let result = res.first(); - assert!(result.is_some()); - assert!(result.unwrap().name.contains("Sodium")); // assume that is Sodium... - Ok(()) - } - - #[tokio::test] - async fn get_single_version_from_project_extra() -> Result<()> { - let api = ModrinthAPI::default(); - let options = ProjectVersionParams { - number: Some("mc1.20.1-0.5.13-fabric"), - loaders: Some(&["fabric"]), - game_versions: Some(&["1.20.1"]), - featured: Some(true), - }; - let result = api.get_project_version("AANobbMI", Some(options)).await?; - - assert!(result.name.contains("Sodium")); // assume that is Sodium... - Ok(()) - } - - #[tokio::test] - async fn get_single_version_from_project_with_wrong_id() -> Result<()> { - let api = ModrinthAPI::default(); - let options = ProjectVersionParams { - number: Some("2"), - loaders: Some(&["fabric"]), - game_versions: Some(&["1.20.1"]), - featured: Some(true), - }; - let result = api.get_project_version("AANobbMI", Some(options)).await; - - assert!(result.is_err()); - Ok(()) - } -} diff --git a/src/structs/versions.rs b/src/structs/versions.rs index 44629d1..8ff7cc7 100644 --- a/src/structs/versions.rs +++ b/src/structs/versions.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; /// The Version struct /// -/// Documentation: https://docs.modrinth.com/api/operations/getprojectversions/#200 +/// Documentation: #[derive(Serialize, Deserialize, Debug)] pub struct Version { pub name: String, diff --git a/tests/search_tests.rs b/tests/search_tests.rs new file mode 100644 index 0000000..7468efe --- /dev/null +++ b/tests/search_tests.rs @@ -0,0 +1,97 @@ +use modrinth_api::ModrinthAPI; +use modrinth_api::structs::projects::ProjectType; +use modrinth_api::structs::search::{ExtendedSearch, Facet, Sort}; + +#[tokio::test] +async fn get_valid_project() -> modrinth_api::Result<()> { + let api = ModrinthAPI::default(); + // HVnmMxH1 -> Complementary Shaders - Reimagined + let response = api.get_project_by_id("HVnmMxH1").await?; + assert_eq!(response.title, "Complementary Shaders - Reimagined"); + Ok(()) +} + +#[tokio::test] +async fn asrt_slug_error() -> modrinth_api::Result<()> { + let api = ModrinthAPI::default(); + let response = api.get_project_by_id("dffdsfdsfsdfdsf").await; + assert!(response.is_err()); + Ok(()) +} + +#[tokio::test] +async fn search_project() -> modrinth_api::Result<()> { + let api = ModrinthAPI::default(); + let response = api + .search( + "xaeros", + &Sort::Downloads, + None, + Some(ExtendedSearch { + offset: None, + facets: vec![vec![Facet::ProjectType(ProjectType::Mod)]], + }), + ) + .await?; + + let response = response.hits.first().unwrap(); + let title = response.slug.as_ref(); + + assert!(title.is_some()); + assert_eq!(title.ok_or(0), Ok(&String::from("xaeros-minimap"))); + Ok(()) +} + +#[tokio::test] +async fn test_fetching_project_with_mut() -> modrinth_api::Result<()> { + let api = ModrinthAPI::default(); + let response = api + .search( + "xaeros", + &Sort::Downloads, + None, + Some(ExtendedSearch { + offset: None, + facets: vec![vec![Facet::ProjectType(ProjectType::Mod)]], + }), + ) + .await?; + + let response = response.hits.first().unwrap(); + let title = response.slug.as_ref(); + + assert!(title.is_some()); + assert_eq!(title.ok_or(0), Ok(&String::from("xaeros-minimap"))); + + let hit = response.to_owned().fetch_project(&api).await?; + + assert_eq!(hit.slug.as_ref().unwrap(), &String::from("xaeros-minimap")); + Ok(()) +} + +#[tokio::test] +async fn test_fetching_project_without_mut() -> modrinth_api::Result<()> { + let api = ModrinthAPI::default(); + let response = api + .search( + "xaeros", + &Sort::Downloads, + None, + Some(ExtendedSearch { + offset: None, + facets: vec![vec![Facet::ProjectType(ProjectType::Mod)]], + }), + ) + .await?; + + let response = response.hits.first().unwrap(); + let title = response.slug.as_ref(); + + assert!(title.is_some()); + assert_eq!(title.ok_or(0), Ok(&String::from("xaeros-minimap"))); + + let hit = response.get_full_project(&api).await?; + + assert_eq!(hit.slug, String::from("xaeros-minimap")); + Ok(()) +} diff --git a/tests/versions_tests.rs b/tests/versions_tests.rs new file mode 100644 index 0000000..e733733 --- /dev/null +++ b/tests/versions_tests.rs @@ -0,0 +1,59 @@ +use modrinth_api::ModrinthAPI; +use modrinth_api::structs::versions::{ProjectVersionParams, ProjectVersionsFilter}; + +#[tokio::test] +async fn get_version_from_project() -> modrinth_api::Result<()> { + let api = ModrinthAPI::default(); + let res = api.get_project_versions("AANobbMI", None).await?; + + let result = res.first(); + assert!(result.is_some()); + assert!(result.unwrap().name.contains("Sodium")); // assume that is Sodium... + Ok(()) +} + +#[tokio::test] +async fn get_version_from_project_extra() -> modrinth_api::Result<()> { + let api = ModrinthAPI::default(); + let options = ProjectVersionsFilter { + loaders: Some(&["fabric"]), + game_versions: Some(&["1.20.1"]), + featured: Some(true), + }; + let res = api.get_project_versions("AANobbMI", Some(options)).await?; + + let result = res.first(); + assert!(result.is_some()); + assert!(result.unwrap().name.contains("Sodium")); // assume that is Sodium... + Ok(()) +} + +#[tokio::test] +async fn get_single_version_from_project_extra() -> modrinth_api::Result<()> { + let api = ModrinthAPI::default(); + let options = ProjectVersionParams { + number: Some("mc1.20.1-0.5.13-fabric"), + loaders: Some(&["fabric"]), + game_versions: Some(&["1.20.1"]), + featured: Some(true), + }; + let result = api.get_project_version("AANobbMI", Some(options)).await?; + + assert!(result.name.contains("Sodium")); // assume that is Sodium... + Ok(()) +} + +#[tokio::test] +async fn get_single_version_from_project_with_wrong_id() -> modrinth_api::Result<()> { + let api = ModrinthAPI::default(); + let options = ProjectVersionParams { + number: Some("2"), + loaders: Some(&["fabric"]), + game_versions: Some(&["1.20.1"]), + featured: Some(true), + }; + let result = api.get_project_version("AANobbMI", Some(options)).await; + + assert!(result.is_err()); + Ok(()) +} From b7e79c4c05bcdb96eea0675a5d2b1f8657793de5 Mon Sep 17 00:00:00 2001 From: nixxoq Date: Sat, 31 May 2025 11:51:28 +0300 Subject: [PATCH 12/12] crate: bump version to 0.2.0 Changes: Features: - Implement `Version`, `ExtraOptions`, `Dependencies`, `DependencyType`, `VersionType`, `Status`, `RequestedStatus`, `File`, `Hash` and `FileType` structures (https://github.com/DevRusty/modrinth-api-rs/commit/e7c6fae7fe898296a51b9500032b0bde3320f317) - Implement `get_project_version`, `get_project_versions` and `get_version_by_id` functions (https://github.com/DevRusty/modrinth-api-rs/commit/8b0503e85654cbbe561a4e8d488a13ee83712779) Deprecated: - Deprecate `extended_search` function. Now it refers to `ModrinthAPI::search` (https://github.com/DevRusty/modrinth-api-rs/commit/c5ae0f8920865791ea572625db33cf844e622731) Removed: - Removed old `search` function (https://github.com/DevRusty/modrinth-api-rs/commit/c5ae0f8920865791ea572625db33cf844e622731) Miscellaneous changes: - ci/cd: testing new ci/cd (#2) - chore(clippy): fix clippy warnings - chore(crate): move all tests to their folder (https://github.com/DevRusty/modrinth-api-rs/commit/a5ff70246ec0c17ae1d297b4a3ac3ff370809d20) --- Cargo.lock | 150 ++++++++++++++++++++++++++++++----------------------- Cargo.toml | 10 ++-- 2 files changed, 91 insertions(+), 69 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 685f0db..3d35ae9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -76,9 +76,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "bumpalo" @@ -94,9 +94,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.21" +version = "1.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" +checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951" dependencies = [ "shlex", ] @@ -166,9 +166,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" dependencies = [ "libc", "windows-sys 0.59.0", @@ -262,9 +262,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", @@ -365,11 +365,10 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.5" +version = "0.27.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +checksum = "03a01595e11bdcec50946522c32dde3fc6914743000a68b93000965f2f02406d" dependencies = [ - "futures-util", "http", "hyper", "hyper-util", @@ -398,22 +397,28 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8" dependencies = [ + "base64", "bytes", "futures-channel", + "futures-core", "futures-util", "http", "http-body", "hyper", + "ipnet", "libc", + "percent-encoding", "pin-project-lite", "socket2", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -489,9 +494,9 @@ checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", @@ -505,9 +510,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" @@ -563,6 +568,16 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "itoa" version = "1.0.15" @@ -622,9 +637,9 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -659,18 +674,18 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "modrinth-api" -version = "0.1.1" +version = "0.2.0" dependencies = [ "chrono", "lazy-regex", @@ -725,9 +740,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "openssl" -version = "0.10.72" +version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ "bitflags", "cfg-if", @@ -757,9 +772,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.108" +version = "0.9.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" dependencies = [ "cc", "libc", @@ -769,9 +784,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -779,9 +794,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", @@ -887,15 +902,14 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.15" +version = "0.12.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +checksum = "e98ff6b0dbbe4d5a37318f433d4fc82babd21631f194d370409ceb2e40b2f0b5" dependencies = [ "base64", "bytes", "encoding_rs", "futures-core", - "futures-util", "h2", "http", "http-body", @@ -912,21 +926,20 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", - "system-configuration", "tokio", "tokio-native-tls", "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows-registry", ] [[package]] @@ -975,15 +988,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" version = "1.12.0" @@ -995,9 +999,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.2" +version = "0.103.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7149975849f1abb3832b246010ef62ccc80d3a76169517ada7188252b9cfb437" +checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" dependencies = [ "ring", "rustls-pki-types", @@ -1006,9 +1010,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "ryu" @@ -1130,9 +1134,9 @@ checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "socket2" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -1204,12 +1208,12 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.19.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom 0.3.3", "once_cell", "rustix", "windows-sys 0.59.0", @@ -1247,9 +1251,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.0" +version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" dependencies = [ "backtrace", "bytes", @@ -1322,6 +1326,24 @@ dependencies = [ "tower-service", ] +[[package]] +name = "tower-http" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -1502,15 +1524,15 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.61.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", "windows-link", "windows-result", - "windows-strings 0.4.0", + "windows-strings 0.4.2", ] [[package]] @@ -1554,9 +1576,9 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ "windows-link", ] @@ -1572,9 +1594,9 @@ dependencies = [ [[package]] name = "windows-strings" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ "windows-link", ] diff --git a/Cargo.toml b/Cargo.toml index 7fbcdc9..794db65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "modrinth-api" -version = "0.1.1" +version = "0.2.0" edition = "2024" [dependencies] reqwest = { version = "0.12.5", features = ["json"] } -tokio = { version = "*", features = ["full"] } -serde = { version = "*", features = ["derive"] } -serde_json = { version = "*" } +tokio = { version = "1.45.1", features = ["full"] } +serde = { version = "1.0.219", features = ["derive"] } +serde_json = { version = "1.0.140" } chrono = { version = "0.4.41", features = ["serde"] } -url = { version = "2.5.4", features = ["serde"] } thiserror = "2.0.12" lazy-regex = "3.4.1" +url = { version = "2.5.4", features = ["serde"] }