Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 86 additions & 64 deletions Cargo.lock

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"] }
11 changes: 0 additions & 11 deletions examples/basic_search.rs

This file was deleted.

8 changes: 4 additions & 4 deletions examples/extended_search.rs → examples/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ use modrinth_api::{Error, ModrinthAPI};
async fn main() -> Result<(), Error> {
let api = ModrinthAPI::default();
let result = api
.extended_search(
"xaeros",
&Sort::Downloads,
Some(20),
.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.
Expand Down
5 changes: 3 additions & 2 deletions src/api/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +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};
22 changes: 0 additions & 22 deletions src/api/projects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
}
114 changes: 17 additions & 97 deletions src/api/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl ModrinthAPI {
/// Ok(())
/// }
/// ```
pub async fn extended_search(
pub async fn search(
&self,
query: &str,
sort: &Sort,
Expand Down Expand Up @@ -92,101 +92,21 @@ 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<u32>) -> Result<Response> {
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
}
}

#[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
.extended_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
.extended_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
.extended_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(())
/// 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<u32>,
extended_search: Option<ExtendedSearch>,
) -> Result<Response> {
self.search(query, sort, limit, extended_search).await
}
}
124 changes: 124 additions & 0 deletions src/api/versions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
//! 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 with custom filtering.
///
/// # Arguments
/// * `project_id` - Project slug/id (`&str`).
/// * `extra_options` - `Option<ProjectVersionsFilter<'_>>` ([`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.
///
/// Example usage of `extra_options`:
/// ```no_run
/// use modrinth_api::structs::versions::ProjectVersionsFilter;
///
/// let options_for_filtered_list = ProjectVersionsFilter {
/// loaders: Some(&["fabric"]),
/// game_versions: Some(&["1.20.1"]),
/// featured: Some(true),
/// };
/// ```
///
/// # Returns
/// `Result<Vec<Version>>`:
/// - `Ok(Vec<Version>)`: 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<ProjectVersionsFilter<'_>>,
) -> Result<Vec<Version>> {
check_id_slug(&[project_id])?;

let mut url = BASE_URL.join_all(vec!["project", project_id, "version"]);

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
}

/// Get the version from the version id
///
/// # Arguments
/// * `version_id` - The ID of the version (`&str`)
///
/// # Returns
///
/// `Result<Version>`:
/// - `Ok(Version)`: [`Version`] struct.
/// - `Err(crate::error::Error)`: An error occurred during the API request or data processing.
pub async fn get_version_by_id(&self, version_id: &str) -> Result<Version> {
check_id_slug(&[version_id])?;
self.client
.get(BASE_URL.join_all(vec!["version", version_id]))
.custom_send_json()
.await
}

/// Retrieves a project [`Version`] with custom filtering.
///
/// # Arguments
/// * `project_id` - Project slug/id (`&str`).
/// * `extra_options` - `Option<ProjectVersionParams<'_>>`: 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<Vec<Version>>`:
/// - `Ok(Vec<Version>)`: 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,
extra_options: Option<ProjectVersionParams<'_>>,
) -> Result<Version> {
check_id_slug(&[project_id])?;

let mut url = BASE_URL.join_all(vec!["project", project_id, "version"]);

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
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> = std::result::Result<T, Error>;

Expand Down
1 change: 1 addition & 0 deletions src/structs/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod projects;
pub mod search;
pub mod versions;

use crate::{ModrinthAPI, Result, structs::projects::Project};
use serde::{Deserialize, Serialize};
Expand Down
Loading
Loading