1
1
//! Endpoint for versions of a crate
2
2
3
- use axum:: extract:: { FromRequestParts , Query } ;
3
+ use axum:: extract:: FromRequestParts ;
4
+ use axum_extra:: extract:: Query ;
4
5
use axum_extra:: json;
5
6
use axum_extra:: response:: ErasedJson ;
6
7
use diesel:: dsl:: not;
@@ -19,6 +20,7 @@ use crate::controllers::krate::CratePath;
19
20
use crate :: models:: { User , Version , VersionOwnerAction } ;
20
21
use crate :: schema:: { users, versions} ;
21
22
use crate :: util:: errors:: { bad_request, AppResult , BoxedAppError } ;
23
+ use crate :: util:: string_excl_null:: StringExclNull ;
22
24
use crate :: util:: RequestUtils ;
23
25
use crate :: views:: EncodableVersion ;
24
26
@@ -41,6 +43,11 @@ pub struct ListQueryParams {
41
43
///
42
44
/// Defaults to `semver`.
43
45
sort : Option < String > ,
46
+
47
+ /// If set, only versions with the specified semver strings are returned.
48
+ #[ serde( rename = "ids[]" , default ) ]
49
+ #[ param( inline) ]
50
+ ids : Vec < StringExclNull > ,
44
51
}
45
52
46
53
impl ListQueryParams {
@@ -123,11 +130,20 @@ async fn list_by_date(
123
130
) -> AppResult < PaginatedVersionsAndPublishers > {
124
131
use seek:: * ;
125
132
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. ids . is_empty ( ) {
141
+ query = query. filter ( versions:: num. eq_any ( params. ids . iter ( ) . map ( |s| s. as_str ( ) ) ) ) ;
142
+ }
143
+ query
144
+ } ;
145
+
146
+ let mut query = make_base_query ( ) ;
131
147
132
148
if let Some ( options) = options {
133
149
assert ! (
@@ -192,11 +208,7 @@ async fn list_by_date(
192
208
// Since the total count is retrieved through an additional query, to maintain consistency
193
209
// with other pagination methods, we only make a count query while data is not empty.
194
210
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 ?
200
212
} else {
201
213
0
202
214
} ;
@@ -229,6 +241,14 @@ async fn list_by_semver(
229
241
use seek:: * ;
230
242
231
243
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. ids . is_empty ( ) {
249
+ query = query. filter ( versions:: num. eq_any ( params. ids . iter ( ) . map ( |s| s. as_str ( ) ) ) ) ;
250
+ }
251
+
232
252
let ( data, total, release_tracks) = if let Some ( options) = options {
233
253
// Since versions will only increase in the future and both sorting and pagination need to
234
254
// happen on the app server, implementing it with fetching only the data needed for sorting
@@ -239,8 +259,7 @@ async fn list_by_semver(
239
259
// while id values are significantly smaller.
240
260
241
261
let mut sorted_versions = IndexMap :: new ( ) ;
242
- versions:: table
243
- . filter ( versions:: crate_id. eq ( crate_id) )
262
+ query
244
263
. select ( ( versions:: id, versions:: num, versions:: yanked) )
245
264
. load_stream :: < ( i32 , String , bool ) > ( conn)
246
265
. await ?
@@ -313,8 +332,7 @@ async fn list_by_semver(
313
332
}
314
333
} else {
315
334
let mut data = IndexMap :: new ( ) ;
316
- versions:: table
317
- . filter ( versions:: crate_id. eq ( crate_id) )
335
+ query
318
336
. left_outer_join ( users:: table)
319
337
. select ( <( Version , Option < User > ) >:: as_select ( ) )
320
338
. load_stream :: < ( Version , Option < User > ) > ( conn)
0 commit comments