Refactor: Return 'static future

Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
Jiahao XU 2024-05-15 23:43:45 +10:00
parent f9008d7c93
commit f90eea4235
No known key found for this signature in database
GPG key ID: 76D1E687CA3C4928
2 changed files with 91 additions and 70 deletions

View file

@ -1,4 +1,4 @@
use std::{sync::OnceLock, time::Duration}; use std::{future::Future, sync::OnceLock, time::Duration};
use binstalk_downloader::remote::{self, header::HeaderMap, StatusCode, Url}; use binstalk_downloader::remote::{self, header::HeaderMap, StatusCode, Url};
use compact_str::CompactString; use compact_str::CompactString;
@ -61,28 +61,34 @@ fn check_http_status_and_header(status: StatusCode, headers: &HeaderMap) -> Resu
} }
} }
pub(super) async fn issue_restful_api<T>( pub(super) fn issue_restful_api<T>(
client: &remote::Client, client: &remote::Client,
path: String, path: String,
auth_token: Option<&str>, auth_token: Option<&str>,
) -> Result<T, GhApiError> ) -> impl Future<Output = Result<T, GhApiError>> + Send + Sync + 'static
where where
T: DeserializeOwned, T: DeserializeOwned,
{ {
let mut request_builder = client let res = Url::parse(&format!("https://api.github.com/{path}")).map(|url| {
.get(Url::parse(&format!("https://api.github.com/{path}"))?) let mut request_builder = client
.header("Accept", "application/vnd.github+json") .get(url)
.header("X-GitHub-Api-Version", "2022-11-28"); .header("Accept", "application/vnd.github+json")
.header("X-GitHub-Api-Version", "2022-11-28");
if let Some(auth_token) = auth_token { if let Some(auth_token) = auth_token {
request_builder = request_builder.bearer_auth(&auth_token); request_builder = request_builder.bearer_auth(&auth_token);
}
request_builder.send(false)
});
async move {
let response = res?.await?;
check_http_status_and_header(response.status(), response.headers())?;
Ok(response.json().await?)
} }
let response = request_builder.send(false).await?;
check_http_status_and_header(response.status(), response.headers())?;
Ok(response.json().await?)
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -107,35 +113,41 @@ fn get_graphql_endpoint() -> &'static Url {
}) })
} }
pub(super) async fn issue_graphql_query<T>( pub(super) fn issue_graphql_query<T>(
client: &remote::Client, client: &remote::Client,
query: String, query: String,
auth_token: &str, auth_token: &str,
) -> Result<T, GhApiError> ) -> impl Future<Output = Result<T, GhApiError>> + Send + Sync + 'static
where where
T: DeserializeOwned, T: DeserializeOwned,
{ {
let graphql_endpoint = get_graphql_endpoint(); let graphql_endpoint = get_graphql_endpoint();
let graphql_query = to_json_string(&GraphQLQuery { query }).map_err(remote::Error::from)?; let res = to_json_string(&GraphQLQuery { query })
.map_err(remote::Error::from)
.map(|graphql_query| {
debug!("Sending graphql query to {graphql_endpoint}: '{graphql_query}'");
debug!("Sending graphql query to {graphql_endpoint}: '{graphql_query}'"); let request_builder = client
.post(graphql_endpoint.clone(), graphql_query)
.header("Accept", "application/vnd.github+json")
.bearer_auth(&auth_token);
let request_builder = client request_builder.send(false)
.post(graphql_endpoint.clone(), graphql_query) });
.header("Accept", "application/vnd.github+json")
.bearer_auth(&auth_token);
let response = request_builder.send(false).await?; async move {
check_http_status_and_header(response.status(), response.headers())?; let response = res?.await?;
check_http_status_and_header(response.status(), response.headers())?;
let response: GraphQLResponse<T> = response.json().await?; let response: GraphQLResponse<T> = response.json().await?;
match response { match response {
GraphQLResponse::Data(data) => Ok(data), GraphQLResponse::Data(data) => Ok(data),
GraphQLResponse::Errors(errors) if errors.is_rate_limited() => { GraphQLResponse::Errors(errors) if errors.is_rate_limited() => {
Err(GhApiError::RateLimit { retry_after: None }) Err(GhApiError::RateLimit { retry_after: None })
}
GraphQLResponse::Errors(errors) => Err(errors.into()),
} }
GraphQLResponse::Errors(errors) => Err(errors.into()),
} }
} }

View file

@ -2,11 +2,12 @@ use std::{
borrow::Borrow, borrow::Borrow,
collections::HashSet, collections::HashSet,
fmt, fmt,
future::Future,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
}; };
use binstalk_downloader::remote::{self}; use binstalk_downloader::remote::{self};
use compact_str::CompactString; use compact_str::{CompactString, ToCompactString};
use serde::Deserialize; use serde::Deserialize;
use super::{ use super::{
@ -65,14 +66,14 @@ impl Artifacts {
} }
} }
async fn fetch_release_artifacts_restful_api( fn fetch_release_artifacts_restful_api(
client: &remote::Client, client: &remote::Client,
GhRelease { GhRelease {
repo: GhRepo { owner, repo }, repo: GhRepo { owner, repo },
tag, tag,
}: &GhRelease, }: &GhRelease,
auth_token: Option<&str>, auth_token: Option<&str>,
) -> Result<Artifacts, GhApiError> { ) -> impl Future<Output = Result<Artifacts, GhApiError>> + Send + Sync + 'static {
issue_restful_api( issue_restful_api(
client, client,
format!( format!(
@ -83,7 +84,6 @@ async fn fetch_release_artifacts_restful_api(
), ),
auth_token, auth_token,
) )
.await
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -132,56 +132,65 @@ impl fmt::Display for FilterCondition {
} }
} }
async fn fetch_release_artifacts_graphql_api( fn fetch_release_artifacts_graphql_api(
client: &remote::Client, client: &remote::Client,
GhRelease { GhRelease {
repo: GhRepo { owner, repo }, repo: GhRepo { owner, repo },
tag, tag,
}: &GhRelease, }: &GhRelease,
auth_token: &str, auth_token: &str,
) -> Result<Artifacts, GhApiError> { ) -> impl Future<Output = Result<Artifacts, GhApiError>> + Send + Sync + 'static {
let mut artifacts = Artifacts::default(); let client = client.clone();
let mut cond = FilterCondition::Init; let auth_token = auth_token.to_compact_string();
loop { let base_query_prefix = format!(
let query = format!( r#"
r#"
query {{ query {{
repository(owner:"{owner}",name:"{repo}") {{ repository(owner:"{owner}",name:"{repo}") {{
release(tagName:"{tag}") {{ release(tagName:"{tag}") {{"#
releaseAssets({cond}) {{ );
nodes {{
name
url
}}
pageInfo {{ endCursor hasNextPage }}
}}
}}
}}
}}"#
);
let data: GraphQLData = issue_graphql_query(client, query, auth_token).await?; let base_query_suffix = r#"
nodes { name url }
pageInfo { endCursor hasNextPage }
}}}}"#
.trim();
let assets = data async move {
.repository let mut artifacts = Artifacts::default();
.and_then(|repository| repository.release) let mut cond = FilterCondition::Init;
.map(|release| release.assets); let base_query_prefix = base_query_prefix.trim();
if let Some(assets) = assets { loop {
artifacts.assets.extend(assets.nodes); let query = format!(
r#"
{base_query_prefix}
releaseAssets({cond}) {{
{base_query_suffix}"#
);
match assets.page_info { let data: GraphQLData = issue_graphql_query(&client, query, &auth_token).await?;
GraphQLPageInfo {
end_cursor: Some(end_cursor), let assets = data
has_next_page: true, .repository
} => { .and_then(|repository| repository.release)
cond = FilterCondition::After(end_cursor); .map(|release| release.assets);
if let Some(assets) = assets {
artifacts.assets.extend(assets.nodes);
match assets.page_info {
GraphQLPageInfo {
end_cursor: Some(end_cursor),
has_next_page: true,
} => {
cond = FilterCondition::After(end_cursor);
}
_ => break Ok(artifacts),
} }
_ => break Ok(artifacts), } else {
break Err(GhApiError::NotFound);
} }
} else {
break Err(GhApiError::NotFound);
} }
} }
} }