11//! Endpoint for versions of a crate
22
3- use axum:: extract:: { FromRequestParts , Query } ;
3+ use axum:: extract:: FromRequestParts ;
4+ use axum_extra:: extract:: Query ;
45use axum_extra:: json;
56use axum_extra:: response:: ErasedJson ;
67use diesel:: dsl:: not;
@@ -19,6 +20,7 @@ use crate::controllers::krate::CratePath;
1920use crate :: models:: { User , Version , VersionOwnerAction } ;
2021use crate :: schema:: { users, versions} ;
2122use crate :: util:: errors:: { bad_request, AppResult , BoxedAppError } ;
23+ use crate :: util:: string_excl_null:: StringExclNull ;
2224use crate :: util:: RequestUtils ;
2325use crate :: views:: EncodableVersion ;
2426
@@ -41,6 +43,11 @@ pub struct ListQueryParams {
4143 ///
4244 /// Defaults to `semver`.
4345 sort : Option < String > ,
46+
47+ /// If set, only versions with the specified semver strings are returned.
48+ #[ serde( rename = "nums[]" , default ) ]
49+ #[ param( inline) ]
50+ nums : Vec < StringExclNull > ,
4451}
4552
4653impl ListQueryParams {
@@ -123,11 +130,20 @@ async fn list_by_date(
123130) -> AppResult < PaginatedVersionsAndPublishers > {
124131 use seek:: * ;
125132
126- let mut query = versions:: table
127- . filter ( versions:: crate_id. eq ( crate_id) )
128- . left_outer_join ( users:: table)
129- . select ( <( Version , Option < User > ) >:: as_select ( ) )
130- . into_boxed ( ) ;
133+ let make_base_query = || {
134+ let mut query = versions:: table
135+ . filter ( versions:: crate_id. eq ( crate_id) )
136+ . left_outer_join ( users:: table)
137+ . select ( <( Version , Option < User > ) >:: as_select ( ) )
138+ . into_boxed ( ) ;
139+
140+ if !params. nums . is_empty ( ) {
141+ query = query. filter ( versions:: num. eq_any ( params. nums . iter ( ) . map ( |s| s. as_str ( ) ) ) ) ;
142+ }
143+ query
144+ } ;
145+
146+ let mut query = make_base_query ( ) ;
131147
132148 if let Some ( options) = options {
133149 assert ! (
@@ -192,11 +208,7 @@ async fn list_by_date(
192208 // Since the total count is retrieved through an additional query, to maintain consistency
193209 // with other pagination methods, we only make a count query while data is not empty.
194210 let total = if !data. is_empty ( ) {
195- versions:: table
196- . filter ( versions:: crate_id. eq ( crate_id) )
197- . count ( )
198- . get_result ( conn)
199- . await ?
211+ make_base_query ( ) . count ( ) . get_result ( conn) . await ?
200212 } else {
201213 0
202214 } ;
@@ -229,6 +241,14 @@ async fn list_by_semver(
229241 use seek:: * ;
230242
231243 let include = params. include ( ) ?;
244+ let mut query = versions:: table
245+ . filter ( versions:: crate_id. eq ( crate_id) )
246+ . into_boxed ( ) ;
247+
248+ if !params. nums . is_empty ( ) {
249+ query = query. filter ( versions:: num. eq_any ( params. nums . iter ( ) . map ( |s| s. as_str ( ) ) ) ) ;
250+ }
251+
232252 let ( data, total, release_tracks) = if let Some ( options) = options {
233253 // Since versions will only increase in the future and both sorting and pagination need to
234254 // happen on the app server, implementing it with fetching only the data needed for sorting
@@ -239,8 +259,7 @@ async fn list_by_semver(
239259 // while id values are significantly smaller.
240260
241261 let mut sorted_versions = IndexMap :: new ( ) ;
242- versions:: table
243- . filter ( versions:: crate_id. eq ( crate_id) )
262+ query
244263 . select ( ( versions:: id, versions:: num, versions:: yanked) )
245264 . load_stream :: < ( i32 , String , bool ) > ( conn)
246265 . await ?
@@ -313,8 +332,7 @@ async fn list_by_semver(
313332 }
314333 } else {
315334 let mut data = IndexMap :: new ( ) ;
316- versions:: table
317- . filter ( versions:: crate_id. eq ( crate_id) )
335+ query
318336 . left_outer_join ( users:: table)
319337 . select ( <( Version , Option < User > ) >:: as_select ( ) )
320338 . load_stream :: < ( Version , Option < User > ) > ( conn)
0 commit comments