Skip to content

Commit

Permalink
impl hangar
Browse files Browse the repository at this point in the history
  • Loading branch information
TheAlan404 committed Jun 19, 2024
1 parent 6d2980b commit 65219b7
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 179 deletions.
128 changes: 39 additions & 89 deletions src/api/sources/hangar/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ use anyhow::{anyhow, Context, Result};

mod models;
pub use models::*;
use serde::de::DeserializeOwned;

use crate::api::{app::App, step::{CacheLocation, FileMeta, Step}, utils::hashing::HashFormat};

const HANGAR_API_URL: &str = "https://hangar.papermc.io/api/v1";

Check warning on line 11 in src/api/sources/hangar/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

constant `HANGAR_API_URL` is never used

warning: constant `HANGAR_API_URL` is never used --> src/api/sources/hangar/mod.rs:11:7 | 11 | const HANGAR_API_URL: &str = "https://hangar.papermc.io/api/v1"; | ^^^^^^^^^^^^^^

Expand All @@ -18,111 +21,58 @@ impl<'a> HangarAPI<'a> {
self.fetch_api(format!("projects/{id}")).await
}

pub async fn fetch_project_versions(&self, id: &str, filter: Option<VersionsFilter>) -> Result<Project> {
todo!();
}

pub async fn fetch_hangar_version(&self, id: &str, version: &str) -> Result<ProjectVersion> {
let filter = self.get_versions_filter();

let version = if version == "latest" {
let versions =
fetch_project_versions(&self.0.http_client, id, Some(filter))
.await?;

versions
.result
.first()
.ok_or(anyhow!("No compatible versions for Hangar project '{id}'"))?
.clone()
} else if version.contains('$') {
let versions =
fetch_project_versions(&self.0.http_client, id, Some(filter))
.await?;

let version = version
.replace("${mcver}", self.0.mc_version())
.replace("${mcversion}", self.0.mc_version());

versions
.result
.iter()
.find(|v| v.name == version)
.cloned()
.or(versions
.result
.iter()
.find(|v| v.name.contains(&version))
.cloned())
.ok_or(anyhow!(
"No compatible versions ('{version}') for Hangar project '{id}'"
))?
} else {
fetch_project_version(&self.0.http_client, id, version).await?
};

Ok(version)
pub async fn fetch_project_versions(&self, id: &str) -> Result<ProjectVersionsResponse> {
self.fetch_api(format!("projects/{id}/versions")).await
}

pub fn get_platform(&self) -> Option<Platform> {
match &self.0.server.jar {
ServerType::Waterfall {} => Some(Platform::Waterfall),
ServerType::Velocity {} => Some(Platform::Velocity),
ServerType::PaperMC { project, .. } if project == "waterfall" => {
Some(Platform::Waterfall)
}
ServerType::PaperMC { project, .. } if project == "velocity" => {
Some(Platform::Velocity)
}
ServerType::PaperMC { project, .. } if project == "paper" => {
Some(Platform::Paper)
}
ServerType::Paper {} | ServerType::Purpur { .. } => {
Some(Platform::Paper)
}
_ => None,
}
pub async fn fetch_project_version(&self, id: &str, version: &str) -> Result<ProjectVersion> {
self.fetch_api(format!("projects/{id}/versions/{version}")).await
}

pub fn get_versions_filter(&self) -> VersionsFilter {
let platform = self.get_platform();
VersionsFilter {
platform_version: if platform.is_some() {
Some(self.0.mc_version().to_owned())
} else {
None
},
platform,
..Default::default()
}
pub fn get_download_url(&self, id: &str, version: &str, platform: &str) -> String {

Check warning on line 32 in src/api/sources/hangar/mod.rs

View workflow job for this annotation

GitHub Actions / clippy

unused `self` argument

warning: unused `self` argument --> src/api/sources/hangar/mod.rs:32:29 | 32 | pub fn get_download_url(&self, id: &str, version: &str, platform: &str) -> String { | ^^^^^ | = help: consider refactoring to an associated function = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unused_self = note: `-W clippy::unused-self` implied by `-W clippy::pedantic` = help: to override `-W clippy::pedantic` add `#[allow(clippy::unused_self)]`
format!(
"{HANGAR_API_URL}/projects/{id}/versions/{version}/{platform}/download"
)
}

#[allow(clippy::cast_sign_loss)]
pub async fn resolve_source(&self, id: &str, version: &str) -> Result<ResolvedFile> {
pub async fn resolve_steps(&self, id: &str, version_id: &str) -> Result<Vec<Step>> {
let version = self
.fetch_hangar_version(id, version)
.fetch_project_version(id, version_id)
.await
.context("Fetching project version")?;

let platform = Platform::Paper; // TODO

let download = version
.downloads
.get(&self.get_platform().unwrap_or(Platform::Paper))
.get(&platform)
.ok_or(anyhow!(
"Platform unsupported for Hangar project '{id}' version '{}'",
version.name
))?;

let cached_file_path = format!("{id}/{}/{}", version.name, download.get_file_info().name);

Ok(ResolvedFile {
url: download.get_url(),
filename: download.get_file_info().name,
cache: CacheStrategy::File {
namespace: Cow::Borrowed("hangar"),
path: cached_file_path,
},
size: Some(download.get_file_info().size_bytes as u64),
hashes: HashMap::from([("sha256".to_owned(), download.get_file_info().sha256_hash)]),
})
let file = download.get_file_info();

let metadata = FileMeta {
cache: Some(CacheLocation("hangar".into(), format!(
"{}/{}/{}_{}",
id.split_once('/').map_or(id, |(_, id)| id),
version.name,
platform.to_string(),
file.name,
))),
filename: file.name,
size: Some(file.size_bytes),
hashes: HashMap::from([
(HashFormat::Sha256, file.sha256_hash),
]),
};

let url = download.get_url();

Ok(vec![
Step::CacheCheck(metadata.clone()),
Step::Download { url, metadata },
])
}
}
91 changes: 1 addition & 90 deletions src/api/sources/hangar/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ impl PlatformVersionDownload {
#[serde(rename_all = "camelCase")]
pub struct FileInfo {
pub name: String,
pub size_bytes: i64,
pub size_bytes: u64,
pub sha256_hash: String,
}

Expand Down Expand Up @@ -293,92 +293,3 @@ pub struct ProjectVersionsResponse {
pub pagination: Pagination,
pub result: Vec<ProjectVersion>,
}

pub async fn fetch_project_versions(
http_client: &reqwest::Client,
id: &str,
filter: Option<VersionsFilter>,
) -> Result<ProjectVersionsResponse> {
let filter = filter.unwrap_or_default();

Ok(http_client
.get(format!(
"{API_V1}/projects/{}/versions",
if let Some((_, post)) = id.split_once('/') {
post
} else {
id
}
))
.query(&filter)
.send()
.await?
.error_for_status()?
.json()
.await?)
}

pub async fn fetch_project_version(
http_client: &reqwest::Client,
id: &str,
name: &str,
) -> Result<ProjectVersion> {
Ok(http_client
.get(format!(
"{API_V1}/projects/{}/versions/{name}",
if let Some((_, post)) = id.split_once('/') {
post
} else {
id
}
))
.send()
.await?
.error_for_status()?
.json()
.await?)
}

pub async fn fetch_latest_project_version(
http_client: &reqwest::Client,
id: &str,
channel: &str,
) -> Result<String> {
Ok(http_client
.get(format!("{API_V1}/projects/{id}/latest"))
.query(&[("channel", channel)])
.send()
.await?
.error_for_status()?
.text()
.await?)
}

pub async fn fetch_latest_project_release(
http_client: &reqwest::Client,
id: &str,
) -> Result<String> {
Ok(http_client
.get(format!("{API_V1}/projects/{id}/latestrelease"))
.send()
.await?
.error_for_status()?
.text()
.await?)
}

pub async fn download_project_version(
http_client: &reqwest::Client,
id: &str,
name: &str,
platform: &Platform,
) -> Result<reqwest::Response> {
Ok(http_client
.get(format!(
"{API_V1}/projects/{id}/versions/{name}/{}/download",
platform.to_string()
))
.send()
.await?
.error_for_status()?)
}

0 comments on commit 65219b7

Please sign in to comment.