66
77use std:: process;
88
9- use serde:: { Deserialize , Serialize } ;
9+ use serde:: { Deserialize , Serialize , de :: DeserializeOwned } ;
1010use tap:: Pipe as _;
1111
1212use crate :: {
13- config:: { BranchName , CommitId , PrNumber } ,
13+ config:: { BranchName , CommitId , PrNumber , RepoName , RepoOwner } ,
1414 git_high_level:: { AvailableBranch , add_remote_branch, find_first_available_branch} ,
1515 utils:: { make_request, normalize_commit_msg, with_uuid} ,
1616} ;
@@ -30,11 +30,27 @@ pub struct Head {
3030 pub r#ref : BranchName ,
3131}
3232
33+ impl GitHubResponse {
34+ /// The endpoint which returns the structure [`GitHubResponse`]
35+ fn endpoint ( repo : & str , pull_request : PrNumber ) -> String {
36+ format ! ( "https://api.github.com/repos/{repo}/pulls/{pull_request}" )
37+ }
38+ }
39+
40+ /// Data returned by endpoint
3341#[ derive( Serialize , Deserialize , Debug ) ]
3442pub struct Repo {
43+ /// e.g. `https://github.com/helix-editor/helix.git`
3544 pub clone_url : String ,
3645}
3746
47+ impl Repo {
48+ /// the endpoint that returns the structure [`Repo`]
49+ pub fn endpoint ( owner : & RepoOwner , repo : & RepoName ) -> String {
50+ format ! ( "https://api.github.com/repos/{owner}/{repo}" , )
51+ }
52+ }
53+
3854#[ derive( Debug ) ]
3955pub struct Branch {
4056 pub upstream_branch_name : BranchName ,
@@ -53,10 +69,16 @@ pub struct RemoteBranch {
5369 pub branch : Branch ,
5470}
5571
72+ /////////////////////////////////////////////////////////
73+
5674/// Make a request to GitHub's API.
5775///
5876/// Either manually fetch the URL or use `gh` CLI
59- async fn gh_api ( url : & str , use_gh_cli : bool ) -> Result < String > {
77+ ///
78+ /// - Outer `Result`: Failed to fetch the URL
79+ /// - Inner `Result`: Failed to deserialize text received by the URL
80+ async fn get_gh_api < T : DeserializeOwned > ( url : & str , use_gh_cli : bool ) -> Result < Result < T > > {
81+ log:: trace!( "making a request to {url}" ) ;
6082 if use_gh_cli {
6183 process:: Command :: new ( "gh" )
6284 . arg ( "api" )
@@ -68,6 +90,11 @@ async fn gh_api(url: &str, use_gh_cli: bool) -> Result<String> {
6890 } else {
6991 make_request ( url) . await
7092 }
93+ . map ( |response| {
94+ serde_json:: from_str :: < T > ( & response) . map_err ( |err| {
95+ anyhow ! ( "failed to parse response.\n {response}. failed to parse because: \n {err}" )
96+ } )
97+ } )
7198}
7299
73100/// Fetch the branch of `remote` at the given `commit`
@@ -77,20 +104,16 @@ pub async fn fetch_branch(
77104) -> Result < ( Repo , RemoteBranch ) > {
78105 let owner = & remote. owner ;
79106 let repo = & remote. repo ;
80- let url = format ! ( "https://api.github.com/repos/{ owner}/{repo}" , ) ;
107+ let url = Repo :: endpoint ( owner, repo ) ;
81108
82- let response = gh_api ( & url, use_gh_cli)
109+ let response = get_gh_api :: < Repo > ( & url, use_gh_cli)
83110 . await
84- . map_err ( |err| anyhow ! ( "failed to fetch branch `{owner}/{repo}`:\n {err}\n " ) ) ?;
85-
86- let response: Repo = serde_json:: from_str ( & response) . map_err ( |err| {
87- anyhow ! ( "failed to parse response.\n {response}. failed to parse because: \n {err}" )
88- } ) ?;
111+ . map_err ( |err| anyhow ! ( "failed to fetch branch `{owner}/{repo}`:\n {err}\n " ) ) ??;
89112
90113 let info = RemoteBranch {
91114 remote : Remote {
92115 repository_url : response. clone_url . clone ( ) ,
93- local_remote_alias : with_uuid ( & format ! ( "{}/{}" , & remote . owner, remote . repo) ) ,
116+ local_remote_alias : with_uuid ( & format ! ( "{}/{}" , & owner, repo) ) ,
94117 } ,
95118 branch : Branch {
96119 local_branch_name : remote. branch . clone ( ) ,
@@ -101,8 +124,8 @@ pub async fn fetch_branch(
101124 add_remote_branch ( & info, remote. commit . as_ref ( ) ) . map_err ( |err| {
102125 anyhow ! (
103126 "Could not add remote branch {}/{}, skipping.\n {err}" ,
104- remote . owner,
105- remote . repo
127+ owner,
128+ repo
106129 )
107130 } ) ?;
108131
@@ -118,24 +141,11 @@ pub async fn fetch_pull_request(
118141 commit_hash : Option < & CommitId > ,
119142 use_gh_cli : bool ,
120143) -> Result < ( GitHubResponse , RemoteBranch ) > {
121- let url = format ! ( "https://api.github.com/repos/{ repo}/pulls/{ pull_request}" ) ;
144+ let url = GitHubResponse :: endpoint ( repo, pull_request) ;
122145
123- let gh_response = if use_gh_cli {
124- process:: Command :: new ( "gh" )
125- . arg ( "api" )
126- . arg ( url)
127- . output ( ) ?
128- . stdout
129- . pipe ( String :: from_utf8) ?
130- } else {
131- make_request ( & url)
132- . await
133- . map_err ( |err| anyhow ! ( "failed to fetch pull request #{pull_request}\n {err}\n " ) ) ?
134- } ;
135-
136- let response: GitHubResponse = serde_json:: from_str ( & gh_response) . map_err ( |err| {
137- anyhow ! ( "failed to parse GitHub response.\n {gh_response}. Could not parse because: \n {err}" )
138- } ) ?;
146+ let response = get_gh_api :: < GitHubResponse > ( & url, use_gh_cli)
147+ . await
148+ . map_err ( |err| anyhow ! ( "failed to fetch pull request #{pull_request}\n {err}\n " ) ) ??;
139149
140150 let remote_branch = RemoteBranch {
141151 remote : Remote {
0 commit comments