@@ -2,15 +2,16 @@ use crate::{
22 AsyncStorage , Config ,
33 db:: {
44 BuildId ,
5- types:: { BuildStatus , version :: Version } ,
5+ types:: BuildStatus ,
66 } ,
77 impl_axum_webpage,
88 web:: {
99 MetaData ,
10+ cache:: CachePolicy ,
1011 error:: { AxumNope , AxumResult } ,
1112 extractors:: { DbConnection , Path , rustdoc:: RustdocParams } ,
1213 file:: File ,
13- filters,
14+ filters, match_version ,
1415 page:: templates:: { RenderBrands , RenderRegular , RenderSolid } ,
1516 } ,
1617} ;
@@ -55,24 +56,37 @@ impl BuildDetailsPage {
5556
5657#[ derive( Clone , Deserialize , Debug ) ]
5758pub ( crate ) struct BuildDetailsParams {
58- pub ( crate ) name : String ,
59- pub ( crate ) version : Version ,
6059 pub ( crate ) id : String ,
6160 pub ( crate ) filename : Option < String > ,
6261}
6362
6463pub ( crate ) async fn build_details_handler (
65- Path ( params) : Path < BuildDetailsParams > ,
64+ params : RustdocParams ,
65+ Path ( build_params) : Path < BuildDetailsParams > ,
6666 mut conn : DbConnection ,
6767 Extension ( config) : Extension < Arc < Config > > ,
6868 Extension ( storage) : Extension < Arc < AsyncStorage > > ,
6969) -> AxumResult < impl IntoResponse > {
70- let id = params
70+ let id = build_params
7171 . id
7272 . parse ( )
7373 . map ( BuildId )
7474 . map_err ( |_| AxumNope :: BuildNotFound ) ?;
7575
76+ let version = match_version ( & mut conn, params. name ( ) , params. req_version ( ) )
77+ . await ?
78+ . assume_exact_name ( ) ?
79+ . into_canonical_req_version_or_else ( |version| {
80+ AxumNope :: Redirect (
81+ params
82+ . clone ( )
83+ . with_req_version ( version)
84+ . build_details_url ( id, build_params. filename . as_deref ( ) ) ,
85+ CachePolicy :: ForeverInCdn ,
86+ )
87+ } ) ?
88+ . into_version ( ) ;
89+
7690 let row = sqlx:: query!(
7791 r#"SELECT
7892 builds.rustc_version,
@@ -87,8 +101,8 @@ pub(crate) async fn build_details_handler(
87101 INNER JOIN crates ON releases.crate_id = crates.id
88102 WHERE builds.id = $1 AND crates.name = $2 AND releases.version = $3"# ,
89103 id. 0 ,
90- params. name,
91- params . version. to_string ( ) ,
104+ params. name( ) ,
105+ version as _
92106 )
93107 . fetch_optional ( & mut * conn)
94108 . await ?
@@ -114,7 +128,7 @@ pub(crate) async fn build_details_handler(
114128 . try_collect ( )
115129 . await ?;
116130
117- let current_filename = if let Some ( filename) = params . filename {
131+ let current_filename = if let Some ( filename) = build_params . filename {
118132 // if we have a given filename in the URL, we use that one.
119133 Some ( filename)
120134 } else if let Some ( default_target) = row. default_target {
@@ -144,8 +158,14 @@ pub(crate) async fn build_details_handler(
144158 ( file_content, all_log_filenames, current_filename)
145159 } ;
146160
147- let metadata = MetaData :: from_crate ( & mut conn, & params. name , & params. version , None ) . await ?;
148- let params = RustdocParams :: from_metadata ( & metadata) ;
161+ let metadata = MetaData :: from_crate (
162+ & mut conn,
163+ params. name ( ) ,
164+ & version,
165+ Some ( params. req_version ( ) . clone ( ) ) ,
166+ )
167+ . await ?;
168+ let params = params. apply_metadata ( & metadata) ;
149169
150170 Ok ( BuildDetailsPage {
151171 metadata,
@@ -167,9 +187,12 @@ pub(crate) async fn build_details_handler(
167187
168188#[ cfg( test) ]
169189mod tests {
170- use crate :: test:: {
171- AxumResponseTestExt , AxumRouterTestExt , FakeBuild , async_wrapper,
172- fake_release_that_failed_before_build,
190+ use crate :: {
191+ db:: types:: { BuildId , ReleaseId } ,
192+ test:: {
193+ AxumResponseTestExt , AxumRouterTestExt , FakeBuild , TestEnvironment , V0_1 ,
194+ async_wrapper, fake_release_that_failed_before_build,
195+ } ,
173196 } ;
174197 use kuchikiki:: traits:: TendrilSink ;
175198 use test_case:: test_case;
@@ -187,6 +210,22 @@ mod tests {
187210 . collect ( )
188211 }
189212
213+ async fn build_ids_for_release (
214+ conn : & mut sqlx:: PgConnection ,
215+ release_id : ReleaseId ,
216+ ) -> Vec < BuildId > {
217+ sqlx:: query!(
218+ "SELECT id FROM builds WHERE rid = $1 ORDER BY id ASC" ,
219+ release_id as _
220+ )
221+ . fetch_all ( conn)
222+ . await
223+ . unwrap ( )
224+ . into_iter ( )
225+ . map ( |row| BuildId ( row. id ) )
226+ . collect ( )
227+ }
228+
190229 #[ test]
191230 fn test_partial_build_result ( ) {
192231 async_wrapper ( |env| async move {
@@ -475,4 +514,30 @@ mod tests {
475514 Ok ( ( ) )
476515 } ) ;
477516 }
517+
518+ #[ tokio:: test( flavor = "multi_thread" ) ]
519+ async fn build_detail_via_latest ( ) -> anyhow:: Result < ( ) > {
520+ let env = TestEnvironment :: new ( ) . await ?;
521+ let rid = env
522+ . fake_release ( )
523+ . await
524+ . name ( "foo" )
525+ . version ( V0_1 )
526+ . create ( )
527+ . await ?;
528+
529+ let mut conn = env. async_db ( ) . async_conn ( ) . await ;
530+ let build_id = {
531+ let ids = build_ids_for_release ( & mut conn, rid) . await ;
532+ assert_eq ! ( ids. len( ) , 1 ) ;
533+ ids[ 0 ]
534+ } ;
535+
536+ env. web_app ( )
537+ . await
538+ . assert_success ( & format ! ( "/crate/foo/latest/builds/{build_id}" ) )
539+ . await ?;
540+
541+ Ok ( ( ) )
542+ }
478543}
0 commit comments