From f90eea423534a31dc85b5d8a07365deaaf7a5eb2 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Wed, 15 May 2024 23:43:45 +1000 Subject: [PATCH] Refactor: Return `'static` future Signed-off-by: Jiahao XU --- .../src/gh_api_client/common.rs | 74 +++++++++------- .../src/gh_api_client/release_artifacts.rs | 87 ++++++++++--------- 2 files changed, 91 insertions(+), 70 deletions(-) diff --git a/crates/binstalk-git-repo-api/src/gh_api_client/common.rs b/crates/binstalk-git-repo-api/src/gh_api_client/common.rs index 82784f68..75442c24 100644 --- a/crates/binstalk-git-repo-api/src/gh_api_client/common.rs +++ b/crates/binstalk-git-repo-api/src/gh_api_client/common.rs @@ -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 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( +pub(super) fn issue_restful_api( client: &remote::Client, path: String, auth_token: Option<&str>, -) -> Result +) -> impl Future> + Send + Sync + 'static where T: DeserializeOwned, { - let mut request_builder = client - .get(Url::parse(&format!("https://api.github.com/{path}"))?) - .header("Accept", "application/vnd.github+json") - .header("X-GitHub-Api-Version", "2022-11-28"); + let res = Url::parse(&format!("https://api.github.com/{path}")).map(|url| { + let mut request_builder = client + .get(url) + .header("Accept", "application/vnd.github+json") + .header("X-GitHub-Api-Version", "2022-11-28"); - if let Some(auth_token) = auth_token { - request_builder = request_builder.bearer_auth(&auth_token); + if let Some(auth_token) = 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)] @@ -107,35 +113,41 @@ fn get_graphql_endpoint() -> &'static Url { }) } -pub(super) async fn issue_graphql_query( +pub(super) fn issue_graphql_query( client: &remote::Client, query: String, auth_token: &str, -) -> Result +) -> impl Future> + Send + Sync + 'static where T: DeserializeOwned, { 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 - .post(graphql_endpoint.clone(), graphql_query) - .header("Accept", "application/vnd.github+json") - .bearer_auth(&auth_token); + request_builder.send(false) + }); - let response = request_builder.send(false).await?; - check_http_status_and_header(response.status(), response.headers())?; + async move { + let response = res?.await?; + check_http_status_and_header(response.status(), response.headers())?; - let response: GraphQLResponse = response.json().await?; + let response: GraphQLResponse = response.json().await?; - match response { - GraphQLResponse::Data(data) => Ok(data), - GraphQLResponse::Errors(errors) if errors.is_rate_limited() => { - Err(GhApiError::RateLimit { retry_after: None }) + match response { + GraphQLResponse::Data(data) => Ok(data), + GraphQLResponse::Errors(errors) if errors.is_rate_limited() => { + Err(GhApiError::RateLimit { retry_after: None }) + } + GraphQLResponse::Errors(errors) => Err(errors.into()), } - GraphQLResponse::Errors(errors) => Err(errors.into()), } } diff --git a/crates/binstalk-git-repo-api/src/gh_api_client/release_artifacts.rs b/crates/binstalk-git-repo-api/src/gh_api_client/release_artifacts.rs index e1ba8686..81ae0ec3 100644 --- a/crates/binstalk-git-repo-api/src/gh_api_client/release_artifacts.rs +++ b/crates/binstalk-git-repo-api/src/gh_api_client/release_artifacts.rs @@ -2,11 +2,12 @@ use std::{ borrow::Borrow, collections::HashSet, fmt, + future::Future, hash::{Hash, Hasher}, }; use binstalk_downloader::remote::{self}; -use compact_str::CompactString; +use compact_str::{CompactString, ToCompactString}; use serde::Deserialize; use super::{ @@ -65,14 +66,14 @@ impl Artifacts { } } -async fn fetch_release_artifacts_restful_api( +fn fetch_release_artifacts_restful_api( client: &remote::Client, GhRelease { repo: GhRepo { owner, repo }, tag, }: &GhRelease, auth_token: Option<&str>, -) -> Result { +) -> impl Future> + Send + Sync + 'static { issue_restful_api( client, format!( @@ -83,7 +84,6 @@ async fn fetch_release_artifacts_restful_api( ), auth_token, ) - .await } #[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, GhRelease { repo: GhRepo { owner, repo }, tag, }: &GhRelease, auth_token: &str, -) -> Result { - let mut artifacts = Artifacts::default(); - let mut cond = FilterCondition::Init; +) -> impl Future> + Send + Sync + 'static { + let client = client.clone(); + let auth_token = auth_token.to_compact_string(); - loop { - let query = format!( - r#" + let base_query_prefix = format!( + r#" query {{ repository(owner:"{owner}",name:"{repo}") {{ - release(tagName:"{tag}") {{ - releaseAssets({cond}) {{ - nodes {{ - name - url - }} - pageInfo {{ endCursor hasNextPage }} - }} - }} - }} -}}"# - ); + release(tagName:"{tag}") {{"# + ); - 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 - .repository - .and_then(|repository| repository.release) - .map(|release| release.assets); + async move { + let mut artifacts = Artifacts::default(); + let mut cond = FilterCondition::Init; + let base_query_prefix = base_query_prefix.trim(); - if let Some(assets) = assets { - artifacts.assets.extend(assets.nodes); + loop { + let query = format!( + r#" +{base_query_prefix} +releaseAssets({cond}) {{ +{base_query_suffix}"# + ); - match assets.page_info { - GraphQLPageInfo { - end_cursor: Some(end_cursor), - has_next_page: true, - } => { - cond = FilterCondition::After(end_cursor); + let data: GraphQLData = issue_graphql_query(&client, query, &auth_token).await?; + + let assets = data + .repository + .and_then(|repository| repository.release) + .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); } } }