11use crate :: {
22 AsyncStorage , Config ,
3- db:: {
4- BuildId ,
5- types:: { BuildStatus , version:: Version } ,
6- } ,
3+ db:: { BuildId , types:: BuildStatus } ,
74 impl_axum_webpage,
85 web:: {
96 MetaData ,
7+ cache:: CachePolicy ,
108 error:: { AxumNope , AxumResult } ,
119 extractors:: { DbConnection , Path , rustdoc:: RustdocParams } ,
1210 file:: File ,
13- filters,
11+ filters, match_version ,
1412 page:: templates:: { RenderBrands , RenderRegular , RenderSolid } ,
1513 } ,
1614} ;
@@ -55,24 +53,37 @@ impl BuildDetailsPage {
5553
5654#[ derive( Clone , Deserialize , Debug ) ]
5755pub ( crate ) struct BuildDetailsParams {
58- pub ( crate ) name : String ,
59- pub ( crate ) version : Version ,
6056 pub ( crate ) id : String ,
6157 pub ( crate ) filename : Option < String > ,
6258}
6359
6460pub ( crate ) async fn build_details_handler (
65- Path ( params) : Path < BuildDetailsParams > ,
61+ params : RustdocParams ,
62+ Path ( build_params) : Path < BuildDetailsParams > ,
6663 mut conn : DbConnection ,
6764 Extension ( config) : Extension < Arc < Config > > ,
6865 Extension ( storage) : Extension < Arc < AsyncStorage > > ,
6966) -> AxumResult < impl IntoResponse > {
70- let id = params
67+ let id = build_params
7168 . id
7269 . parse ( )
7370 . map ( BuildId )
7471 . map_err ( |_| AxumNope :: BuildNotFound ) ?;
7572
73+ let version = match_version ( & mut conn, params. name ( ) , params. req_version ( ) )
74+ . await ?
75+ . assume_exact_name ( ) ?
76+ . into_canonical_req_version_or_else ( |version| {
77+ AxumNope :: Redirect (
78+ params
79+ . clone ( )
80+ . with_req_version ( version)
81+ . build_details_url ( id, build_params. filename . as_deref ( ) ) ,
82+ CachePolicy :: ForeverInCdn ,
83+ )
84+ } ) ?
85+ . into_version ( ) ;
86+
7687 let row = sqlx:: query!(
7788 r#"SELECT
7889 builds.rustc_version,
@@ -87,8 +98,8 @@ pub(crate) async fn build_details_handler(
8798 INNER JOIN crates ON releases.crate_id = crates.id
8899 WHERE builds.id = $1 AND crates.name = $2 AND releases.version = $3"# ,
89100 id. 0 ,
90- params. name,
91- params . version. to_string ( ) ,
101+ params. name( ) ,
102+ version as _
92103 )
93104 . fetch_optional ( & mut * conn)
94105 . await ?
@@ -114,7 +125,7 @@ pub(crate) async fn build_details_handler(
114125 . try_collect ( )
115126 . await ?;
116127
117- let current_filename = if let Some ( filename) = params . filename {
128+ let current_filename = if let Some ( filename) = build_params . filename {
118129 // if we have a given filename in the URL, we use that one.
119130 Some ( filename)
120131 } else if let Some ( default_target) = row. default_target {
@@ -144,8 +155,14 @@ pub(crate) async fn build_details_handler(
144155 ( file_content, all_log_filenames, current_filename)
145156 } ;
146157
147- let metadata = MetaData :: from_crate ( & mut conn, & params. name , & params. version , None ) . await ?;
148- let params = RustdocParams :: from_metadata ( & metadata) ;
158+ let metadata = MetaData :: from_crate (
159+ & mut conn,
160+ params. name ( ) ,
161+ & version,
162+ Some ( params. req_version ( ) . clone ( ) ) ,
163+ )
164+ . await ?;
165+ let params = params. apply_metadata ( & metadata) ;
149166
150167 Ok ( BuildDetailsPage {
151168 metadata,
@@ -167,9 +184,12 @@ pub(crate) async fn build_details_handler(
167184
168185#[ cfg( test) ]
169186mod tests {
170- use crate :: test:: {
171- AxumResponseTestExt , AxumRouterTestExt , FakeBuild , async_wrapper,
172- fake_release_that_failed_before_build,
187+ use crate :: {
188+ db:: types:: { BuildId , ReleaseId } ,
189+ test:: {
190+ AxumResponseTestExt , AxumRouterTestExt , FakeBuild , TestEnvironment , V0_1 ,
191+ async_wrapper, fake_release_that_failed_before_build,
192+ } ,
173193 } ;
174194 use kuchikiki:: traits:: TendrilSink ;
175195 use test_case:: test_case;
@@ -187,6 +207,22 @@ mod tests {
187207 . collect ( )
188208 }
189209
210+ async fn build_ids_for_release (
211+ conn : & mut sqlx:: PgConnection ,
212+ release_id : ReleaseId ,
213+ ) -> Vec < BuildId > {
214+ sqlx:: query!(
215+ "SELECT id FROM builds WHERE rid = $1 ORDER BY id ASC" ,
216+ release_id as _
217+ )
218+ . fetch_all ( conn)
219+ . await
220+ . unwrap ( )
221+ . into_iter ( )
222+ . map ( |row| BuildId ( row. id ) )
223+ . collect ( )
224+ }
225+
190226 #[ test]
191227 fn test_partial_build_result ( ) {
192228 async_wrapper ( |env| async move {
@@ -475,4 +511,30 @@ mod tests {
475511 Ok ( ( ) )
476512 } ) ;
477513 }
514+
515+ #[ tokio:: test( flavor = "multi_thread" ) ]
516+ async fn build_detail_via_latest ( ) -> anyhow:: Result < ( ) > {
517+ let env = TestEnvironment :: new ( ) . await ?;
518+ let rid = env
519+ . fake_release ( )
520+ . await
521+ . name ( "foo" )
522+ . version ( V0_1 )
523+ . create ( )
524+ . await ?;
525+
526+ let mut conn = env. async_db ( ) . async_conn ( ) . await ;
527+ let build_id = {
528+ let ids = build_ids_for_release ( & mut conn, rid) . await ;
529+ assert_eq ! ( ids. len( ) , 1 ) ;
530+ ids[ 0 ]
531+ } ;
532+
533+ env. web_app ( )
534+ . await
535+ . assert_success ( & format ! ( "/crate/foo/latest/builds/{build_id}" ) )
536+ . await ?;
537+
538+ Ok ( ( ) )
539+ }
478540}
0 commit comments