mod query_string;
pub mod search;
use {async_stream::try_stream, futures_core::stream::Stream};
use crate::{
base62::Base62,
query::{query_string::JsonQueryParams, search::*},
types::{FileHashes, Project, ProjectIdentifier, ProjectVersion},
};
static MODRINTH_ENDPOINT: &str = "https://api.modrinth.com/v2/";
#[derive(Clone, Debug)]
pub struct Client {
inner: surf::Client,
}
impl Client {
pub fn new(token: Option<String>) -> Self {
let mut config = surf::Config::new();
config = config.set_base_url(surf::Url::parse(MODRINTH_ENDPOINT).unwrap());
if let Some(token) = token {
config = config.add_header("Authorization", token).unwrap();
}
Self {
inner: config.try_into().unwrap(),
}
}
pub fn with_config(
mut config: surf::Config,
) -> Result<Self, <surf::Client as TryFrom<surf::Config>>::Error> {
config = config.set_base_url(surf::Url::parse(MODRINTH_ENDPOINT).unwrap());
Ok(Self {
inner: config.try_into()?,
})
}
pub async fn search(&self, params: &SearchParams) -> surf::Result<SearchResults> {
self.inner
.get(&format!("search?{}", ¶ms.to_query_string()))
.recv_json()
.await
}
pub fn search_iter(
&self,
mut params: SearchParams,
) -> impl Stream<Item = surf::Result<ProjectResult>> + '_ {
try_stream! {
let (mut projects, total) = {
let limit = std::mem::replace(&mut params.limit, Some(1));
let search = self.search(¶ms).await?;
params.limit = limit;
params.offset = Some(params.offset.unwrap_or(0) + 1);
(search.hits, search.total_hits)
};
loop {
if projects.is_empty() {
if params.offset.unwrap() >= total {
break
}
projects = self.search(¶ms).await?.hits;
params.offset = Some(params.offset.unwrap() + projects.len())
}
yield projects.pop_front().unwrap()
}
}
}
pub async fn get_project(&self, identifier: &ProjectIdentifier) -> surf::Result<Project> {
self.inner
.get(&format!("project/{}", identifier))
.recv_json()
.await
}
pub async fn get_project_versions(
&self,
identifier: &ProjectIdentifier,
) -> surf::Result<Vec<ProjectVersion>> {
self.inner
.get(&format!("version/{}", identifier))
.recv_json()
.await
}
pub async fn get_version(&self, identifier: &Base62) -> surf::Result<ProjectVersion> {
self.inner
.get(&format!("version/{}", identifier))
.recv_json()
.await
}
pub async fn get_version_by_hash(&self, hash: &FileHashes) -> surf::Result<ProjectVersion> {
self.inner
.get(&match hash {
FileHashes {
sha512: Some(hash), ..
} => format!("version_file/{}?algorithm=sha512", hash),
FileHashes {
sha1: Some(hash), ..
} => format!("version_file/{}?algorithm=sha1", hash),
_ => panic!("expected at least one field of `hash` to be `Some`"),
})
.recv_json()
.await
}
}