@@ -26,6 +26,22 @@ struct Release {
26
26
target_name : Option < String > ,
27
27
rustdoc_status : bool ,
28
28
release_time : time:: Timespec ,
29
+ stars : i32 ,
30
+ }
31
+
32
+
33
+ impl Default for Release {
34
+ fn default ( ) -> Release {
35
+ Release {
36
+ name : String :: new ( ) ,
37
+ version : String :: new ( ) ,
38
+ description : None ,
39
+ target_name : None ,
40
+ rustdoc_status : false ,
41
+ release_time : time:: get_time ( ) ,
42
+ stars : 0 ,
43
+ }
44
+ }
29
45
}
30
46
31
47
@@ -39,36 +55,59 @@ impl ToJson for Release {
39
55
m. insert ( "rustdoc_status" . to_string ( ) , self . rustdoc_status . to_json ( ) ) ;
40
56
m. insert ( "release_time" . to_string ( ) ,
41
57
duration_to_str ( self . release_time ) . to_json ( ) ) ;
58
+ m. insert ( "stars" . to_string ( ) , self . stars . to_json ( ) ) ;
42
59
m. to_json ( )
43
60
}
44
61
}
45
62
46
63
47
- fn get_releases ( conn : & Connection , page : i64 , limit : i64 ) -> Vec < Release > {
48
- let mut packages = Vec :: new ( ) ;
64
+ enum Order {
65
+ ReleaseTime , // this is default order
66
+ GithubStars ,
67
+ }
68
+
69
+
70
+ fn get_releases ( conn : & Connection , page : i64 , limit : i64 , order : Order ) -> Vec < Release > {
49
71
50
72
let offset = ( page - 1 ) * limit;
51
73
52
- for row in & conn. query ( "SELECT crates.name, \
53
- releases.version, \
54
- releases.description, \
55
- releases.target_name, \
56
- releases.release_time, \
57
- releases.rustdoc_status \
58
- FROM crates \
59
- INNER JOIN releases ON crates.id = releases.crate_id \
60
- ORDER BY releases.release_time DESC \
61
- LIMIT $1 OFFSET $2",
62
- & [ & limit, & offset] )
63
- . unwrap ( ) {
74
+ // TODO: This function changed so much during development and current version have code
75
+ // repeats for queries. There is definitely room for improvements.
76
+ let query = match order {
77
+ Order :: ReleaseTime => "SELECT crates.name, \
78
+ releases.version, \
79
+ releases.description, \
80
+ releases.target_name, \
81
+ releases.release_time, \
82
+ releases.rustdoc_status, \
83
+ crates.github_stars \
84
+ FROM crates \
85
+ INNER JOIN releases ON crates.id = releases.crate_id \
86
+ ORDER BY releases.release_time DESC \
87
+ LIMIT $1 OFFSET $2",
88
+ Order :: GithubStars => "SELECT crates.name, \
89
+ releases.version, \
90
+ releases.description, \
91
+ releases.target_name, \
92
+ releases.release_time, \
93
+ releases.rustdoc_status, \
94
+ crates.github_stars \
95
+ FROM crates \
96
+ INNER JOIN releases ON releases.id = crates.latest_version_id \
97
+ ORDER BY crates.github_stars DESC \
98
+ LIMIT $1 OFFSET $2",
99
+ } ;
64
100
101
+ let mut packages = Vec :: new ( ) ;
102
+ for row in & conn. query ( & query, & [ & limit, & offset] ) . unwrap ( ) {
65
103
let package = Release {
66
104
name : row. get ( 0 ) ,
67
105
version : row. get ( 1 ) ,
68
106
description : row. get ( 2 ) ,
69
107
target_name : row. get ( 3 ) ,
70
108
release_time : row. get ( 4 ) ,
71
109
rustdoc_status : row. get ( 5 ) ,
110
+ stars : row. get ( 6 ) ,
72
111
} ;
73
112
74
113
packages. push ( package) ;
@@ -110,6 +149,7 @@ fn get_search_results(conn: &Connection,
110
149
target_name : row. get ( 3 ) ,
111
150
release_time : row. get ( 4 ) ,
112
151
rustdoc_status : row. get ( 5 ) ,
152
+ ..Release :: default ( )
113
153
} ;
114
154
115
155
packages. push ( package) ;
@@ -131,7 +171,7 @@ fn get_search_results(conn: &Connection,
131
171
132
172
pub fn home_page ( req : & mut Request ) -> IronResult < Response > {
133
173
let conn = req. extensions . get :: < Pool > ( ) . unwrap ( ) ;
134
- let packages = get_releases ( conn, 1 , RELEASES_IN_HOME ) ;
174
+ let packages = get_releases ( conn, 1 , RELEASES_IN_HOME , Order :: ReleaseTime ) ;
135
175
Page :: new ( packages)
136
176
. set_true ( "show_search_form" )
137
177
. set_true ( "hide_package_navigation" )
@@ -140,7 +180,6 @@ pub fn home_page(req: &mut Request) -> IronResult<Response> {
140
180
141
181
142
182
pub fn releases_handler ( req : & mut Request ) -> IronResult < Response > {
143
-
144
183
// page number of releases
145
184
let page_number: i64 = req. extensions
146
185
. get :: < Router > ( )
@@ -151,31 +190,68 @@ pub fn releases_handler(req: &mut Request) -> IronResult<Response> {
151
190
. unwrap_or ( 1 ) ;
152
191
153
192
let conn = req. extensions . get :: < Pool > ( ) . unwrap ( ) ;
154
- let packages = get_releases ( conn, page_number, RELEASES_IN_RELEASES ) ;
155
- let page = {
156
- let page = Page :: new ( packages)
157
- . title ( "Recent Releases" )
158
- . set_int ( "next_page" , page_number + 1 ) ;
159
-
160
- // Set previous page if we are not in first page
161
- // TODO: Currently, there is no way to know we are on the last page.
162
- // TBH I kinda don't care. COUNT(*) is expensive, and there is more than
163
- // 25k release anyway, I don't think people will check last page. I can cache
164
- // result and use this value for approximation. But since I don't know how to
165
- // do it yet, I will just skip page checking. I can also assume if Package count
166
- // is less than RELEASES_IN_RELEASES, we are on last page.
167
- if page_number == 1 {
168
- page
169
- } else {
170
- page. set_int ( "previous_page" , page_number - 1 )
171
- }
172
- } ;
193
+ let packages = get_releases ( conn, page_number, RELEASES_IN_RELEASES , Order :: ReleaseTime ) ;
173
194
195
+ if packages. is_empty ( ) {
196
+ return Err ( IronError :: new ( NoCrate , status:: NotFound ) ) ;
197
+ }
174
198
175
- page. set_int ( "next_page" , page_number + 1 ) . to_resp ( "releases" )
199
+ // Show next and previous page buttons
200
+ // This is a temporary solution to avoid expensive COUNT(*)
201
+ let ( show_next_page, show_previous_page) = ( packages. len ( ) == RELEASES_IN_RELEASES as usize ,
202
+ page_number != 1 ) ;
203
+
204
+ Page :: new ( packages)
205
+ . title ( "Releases" )
206
+ . set ( "description" , "Recently uploaded crates" )
207
+ . set ( "release_type" , "recent" )
208
+ . set_true ( "show_releases_navigation" )
209
+ . set_true ( "releases_navigation_recent_tab" )
210
+ . set_bool ( "show_next_page_button" , show_next_page)
211
+ . set_int ( "next_page" , page_number + 1 )
212
+ . set_bool ( "show_previous_page_button" , show_previous_page)
213
+ . set_int ( "previous_page" , page_number - 1 )
214
+ . to_resp ( "releases" )
176
215
}
177
216
178
217
218
+ // TODO: This function is almost identical to previous one
219
+ pub fn stars_handler ( req : & mut Request ) -> IronResult < Response > {
220
+ // page number of releases
221
+ let page_number: i64 = req. extensions
222
+ . get :: < Router > ( )
223
+ . unwrap ( )
224
+ . find ( "page" )
225
+ . unwrap_or ( "1" )
226
+ . parse ( )
227
+ . unwrap_or ( 1 ) ;
228
+
229
+ let conn = req. extensions . get :: < Pool > ( ) . unwrap ( ) ;
230
+ let packages = get_releases ( conn, page_number, RELEASES_IN_RELEASES , Order :: GithubStars ) ;
231
+
232
+ if packages. is_empty ( ) {
233
+ return Err ( IronError :: new ( NoCrate , status:: NotFound ) ) ;
234
+ }
235
+
236
+ // Show next and previous page buttons
237
+ // This is a temporary solution to avoid expensive COUNT(*)
238
+ let ( show_next_page, show_previous_page) = ( packages. len ( ) == RELEASES_IN_RELEASES as usize ,
239
+ page_number != 1 ) ;
240
+
241
+ Page :: new ( packages)
242
+ . title ( "Releases" )
243
+ . set ( "description" , "Most starred crates" )
244
+ . set ( "release_type" , "stars" )
245
+ . set_true ( "show_releases_navigation" )
246
+ . set_true ( "releases_navigation_stars_tab" )
247
+ . set_true ( "show_stars" )
248
+ . set_bool ( "show_next_page_button" , show_next_page)
249
+ . set_int ( "next_page" , page_number + 1 )
250
+ . set_bool ( "show_previous_page_button" , show_previous_page)
251
+ . set_int ( "previous_page" , page_number - 1 )
252
+ . to_resp ( "releases" )
253
+ }
254
+
179
255
180
256
pub fn search_handler ( req : & mut Request ) -> IronResult < Response > {
181
257
use params:: { Params , Value } ;
0 commit comments