@@ -5,8 +5,9 @@ use std::{
55 collections:: { BTreeMap , HashMap , HashSet } ,
66} ;
77
8- use crate :: util:: diesel:: Conn ;
9- use diesel:: { connection:: DefaultLoadingMode , QueryResult } ;
8+ use crate :: util:: diesel:: prelude:: * ;
9+ use diesel_async:: { AsyncPgConnection , RunQueryDsl } ;
10+ use futures_util:: TryStreamExt ;
1011use typomania:: { AuthorSet , Corpus , Package } ;
1112
1213/// A corpus of the current top crates on crates.io, as determined by their download counts, along
@@ -18,12 +19,11 @@ pub struct TopCrates {
1819
1920impl TopCrates {
2021 /// Retrieves the `num` top crates from the database.
21- pub fn new ( conn : & mut impl Conn , num : i64 ) -> QueryResult < Self > {
22+ pub async fn new ( conn : & mut AsyncPgConnection , num : i64 ) -> QueryResult < Self > {
2223 use crate :: {
2324 models,
2425 schema:: { crate_downloads, crate_owners} ,
2526 } ;
26- use diesel:: prelude:: * ;
2727
2828 // We have to build up a data structure that contains the top crates, their owners in some
2929 // form that is easily compared, and that can be indexed by the crate name.
@@ -42,45 +42,51 @@ impl TopCrates {
4242 // Once we have the results of those queries, we can glom it all together into one happy
4343 // data structure.
4444
45- let mut crates: BTreeMap < i32 , ( String , Crate ) > = BTreeMap :: new ( ) ;
46- for result in models:: Crate :: all ( )
45+ let crates: BTreeMap < i32 , ( String , Crate ) > = BTreeMap :: new ( ) ;
46+ let crates = models:: Crate :: all ( )
4747 . inner_join ( crate_downloads:: table)
4848 . order ( crate_downloads:: downloads. desc ( ) )
4949 . limit ( num)
50- . load_iter :: < models:: Crate , DefaultLoadingMode > ( conn) ?
51- {
52- let krate = result?;
53- crates. insert (
54- krate. id ,
55- (
56- krate. name ,
57- Crate {
58- owners : HashSet :: new ( ) ,
59- } ,
60- ) ,
61- ) ;
62- }
50+ . load_stream :: < models:: Crate > ( conn)
51+ . await ?
52+ . try_fold ( crates, |mut crates, krate| {
53+ crates. insert (
54+ krate. id ,
55+ (
56+ krate. name ,
57+ Crate {
58+ owners : HashSet :: new ( ) ,
59+ } ,
60+ ) ,
61+ ) ;
62+
63+ futures_util:: future:: ready ( Ok ( crates) )
64+ } )
65+ . await ?;
6366
6467 // This query might require more low level knowledge of crate_owners than we really want
6568 // outside of the models module. It would probably make more sense in the long term to have
6669 // this live in the Owner type, but for now I want to keep the typosquatting logic as
6770 // self-contained as possible in case we decide not to go ahead with this in the longer
6871 // term.
69- for result in crate_owners:: table
72+ let crates = crate_owners:: table
7073 . filter ( crate_owners:: deleted. eq ( false ) )
7174 . filter ( crate_owners:: crate_id. eq_any ( crates. keys ( ) . cloned ( ) . collect :: < Vec < _ > > ( ) ) )
7275 . select ( (
7376 crate_owners:: crate_id,
7477 crate_owners:: owner_id,
7578 crate_owners:: owner_kind,
7679 ) )
77- . load_iter :: < ( i32 , i32 , i32 ) , DefaultLoadingMode > ( conn) ?
78- {
79- let ( crate_id, owner_id, owner_kind) = result?;
80- crates. entry ( crate_id) . and_modify ( |( _name, krate) | {
81- krate. owners . insert ( Owner :: new ( owner_id, owner_kind) ) ;
82- } ) ;
83- }
80+ . load_stream :: < ( i32 , i32 , i32 ) > ( conn)
81+ . await ?
82+ . try_fold ( crates, |mut crates, ( crate_id, owner_id, owner_kind) | {
83+ crates. entry ( crate_id) . and_modify ( |( _name, krate) | {
84+ krate. owners . insert ( Owner :: new ( owner_id, owner_kind) ) ;
85+ } ) ;
86+
87+ futures_util:: future:: ready ( Ok ( crates) )
88+ } )
89+ . await ?;
8490
8591 Ok ( Self {
8692 crates : crates. into_values ( ) . collect ( ) ,
@@ -104,12 +110,16 @@ pub struct Crate {
104110
105111impl Crate {
106112 /// Hydrates a crate and its owners from the database given the crate name.
107- pub fn from_name ( conn : & mut impl Conn , name : & str ) -> QueryResult < Self > {
113+ pub async fn from_name ( conn : & mut AsyncPgConnection , name : & str ) -> QueryResult < Self > {
108114 use crate :: models;
109- use diesel:: prelude:: * ;
110115
111- let krate = models:: Crate :: by_exact_name ( name) . first ( conn) ?;
112- let owners = krate. owners ( conn) ?. into_iter ( ) . map ( Owner :: from) . collect ( ) ;
116+ let krate = models:: Crate :: by_exact_name ( name) . first ( conn) . await ?;
117+ let owners = krate
118+ . async_owners ( conn)
119+ . await ?
120+ . into_iter ( )
121+ . map ( Owner :: from)
122+ . collect ( ) ;
113123
114124 Ok ( Self { owners } )
115125 }
@@ -166,10 +176,11 @@ mod tests {
166176 use super :: * ;
167177 use crate :: typosquat:: test_util:: faker;
168178 use crates_io_test_db:: TestDatabase ;
179+ use diesel_async:: AsyncConnection ;
169180 use thiserror:: Error ;
170181
171- #[ test]
172- fn top_crates ( ) -> Result < ( ) , Error > {
182+ #[ tokio :: test]
183+ async fn top_crates ( ) -> Result < ( ) , Error > {
173184 let test_db = TestDatabase :: new ( ) ;
174185 let mut conn = test_db. connect ( ) ;
175186
@@ -187,7 +198,8 @@ mod tests {
187198 faker:: add_crate_to_team ( & mut conn, & user_b, & top_b, & not_the_a_team) ?;
188199 faker:: add_crate_to_team ( & mut conn, & user_b, & not_top_c, & not_the_a_team) ?;
189200
190- let top_crates = TopCrates :: new ( & mut conn, 2 ) ?;
201+ let mut async_conn = AsyncPgConnection :: establish ( test_db. url ( ) ) . await ?;
202+ let top_crates = TopCrates :: new ( & mut async_conn, 2 ) . await ?;
191203
192204 // Let's ensure the top crates include what we expect (which is a and b, since we asked for
193205 // 2 crates and they're the most downloaded).
@@ -201,7 +213,7 @@ mod tests {
201213 assert ! ( !pkg_a. shared_authors( pkg_b. authors( ) ) ) ;
202214
203215 // Now let's go get package c and pretend it's a new package.
204- let pkg_c = Crate :: from_name ( & mut conn , "c" ) ?;
216+ let pkg_c = Crate :: from_name ( & mut async_conn , "c" ) . await ?;
205217
206218 // c _does_ have an author in common with a.
207219 assert ! ( pkg_a. shared_authors( pkg_c. authors( ) ) ) ;
@@ -227,5 +239,8 @@ mod tests {
227239
228240 #[ error( transparent) ]
229241 Diesel ( #[ from] diesel:: result:: Error ) ,
242+
243+ #[ error( transparent) ]
244+ Connection ( #[ from] ConnectionError ) ,
230245 }
231246}
0 commit comments