@@ -141,6 +141,91 @@ pub async fn gh_range_diff(
141141 )
142142}
143143
144+ /// Compute and renders an emulated `git range-diff` between two pushes (old and new).
145+ ///
146+ /// - `oldbasehead` is `OLDBASE..OLDHEAD`
147+ /// - `newbasehead` is `NEWBASE..NEWHEAD`
148+ pub async fn gh_ranges_diff (
149+ Path ( ( owner, repo, oldbasehead, newbasehead) ) : Path < ( String , String , String , String ) > ,
150+ State ( ctx) : State < Arc < Context > > ,
151+ host : Host ,
152+ ) -> axum:: response:: Result < impl IntoResponse , AppError > {
153+ let Some ( ( oldbase, oldhead) ) = oldbasehead. split_once ( ".." ) else {
154+ return Ok ( (
155+ StatusCode :: BAD_REQUEST ,
156+ HeaderMap :: new ( ) ,
157+ format ! ( "`{oldbasehead}` is not in the form `base..head`" ) ,
158+ ) ) ;
159+ } ;
160+
161+ let Some ( ( newbase, newhead) ) = newbasehead. split_once ( ".." ) else {
162+ return Ok ( (
163+ StatusCode :: BAD_REQUEST ,
164+ HeaderMap :: new ( ) ,
165+ format ! ( "`{newbasehead}` is not in the form `base..head`" ) ,
166+ ) ) ;
167+ } ;
168+
169+ let repos = ctx
170+ . team
171+ . repos ( )
172+ . await
173+ . context ( "unable to retrieve team repos" ) ?;
174+
175+ // Verify that the request org is part of the Rust project
176+ let Some ( repos) = repos. repos . get ( & owner) else {
177+ return Ok ( (
178+ StatusCode :: BAD_REQUEST ,
179+ HeaderMap :: new ( ) ,
180+ format ! ( "organization `{owner}` is not part of the Rust Project team repos" ) ,
181+ ) ) ;
182+ } ;
183+
184+ // Verify that the request repo is part of the Rust project
185+ if !repos. iter ( ) . any ( |r| r. name == repo) {
186+ return Ok ( (
187+ StatusCode :: BAD_REQUEST ,
188+ HeaderMap :: new ( ) ,
189+ format ! ( "repository `{owner}` is not part of the Rust Project team repos" ) ,
190+ ) ) ;
191+ }
192+
193+ let issue_repo = github:: IssueRepository {
194+ organization : owner. to_string ( ) ,
195+ repository : repo. to_string ( ) ,
196+ } ;
197+
198+ // Get the comparison between the oldbase..oldhead
199+ let old = async {
200+ ctx. github
201+ . compare ( & issue_repo, & oldbase, oldhead)
202+ . await
203+ . with_context ( || {
204+ format ! ( "failed to retrive the comparison between {oldbase} and {oldhead}" )
205+ } )
206+ } ;
207+
208+ // Get the comparison between the newbase..newhead
209+ let new = async {
210+ ctx. github
211+ . compare ( & issue_repo, & newbase, newhead)
212+ . await
213+ . with_context ( || {
214+ format ! ( "failed to retrive the comparison between {newbase} and {newhead}" )
215+ } )
216+ } ;
217+
218+ // Wait for both futures and early exit if there is an error
219+ let ( old, new) = futures:: try_join!( old, new) ?;
220+
221+ process_old_new (
222+ host,
223+ ( & owner, & repo) ,
224+ ( & oldbase, oldhead, old) ,
225+ ( & newbase, newhead, new) ,
226+ )
227+ }
228+
144229fn process_old_new (
145230 Host ( host) : Host ,
146231 ( owner, repo) : ( & str , & str ) ,
0 commit comments