@@ -10,7 +10,6 @@ use crate::util::cache_lock::CacheLockMode;
10
10
use crate :: util:: context:: GlobalContext ;
11
11
use crate :: util:: style;
12
12
use crate :: util:: CargoResult ;
13
- use anstyle:: Style ;
14
13
use std:: cmp:: Ordering ;
15
14
use std:: collections:: { BTreeMap , HashSet } ;
16
15
use tracing:: debug;
@@ -26,17 +25,19 @@ pub struct UpdateOptions<'a> {
26
25
27
26
pub fn generate_lockfile ( ws : & Workspace < ' _ > ) -> CargoResult < ( ) > {
28
27
let mut registry = PackageRegistry :: new ( ws. gctx ( ) ) ?;
28
+ let previous_resolve = None ;
29
29
let mut resolve = ops:: resolve_with_previous (
30
30
& mut registry,
31
31
ws,
32
32
& CliFeatures :: new_all ( true ) ,
33
33
HasDevUnits :: Yes ,
34
- None ,
34
+ previous_resolve ,
35
35
None ,
36
36
& [ ] ,
37
37
true ,
38
38
) ?;
39
39
ops:: write_pkg_lockfile ( ws, & mut resolve) ?;
40
+ print_lockfile_changes ( ws. gctx ( ) , previous_resolve, & resolve, & mut registry) ?;
40
41
Ok ( ( ) )
41
42
}
42
43
@@ -154,7 +155,7 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
154
155
true ,
155
156
) ?;
156
157
157
- print_lockfile_update ( opts. gctx , & previous_resolve, & resolve, & mut registry) ?;
158
+ print_lockfile_updates ( opts. gctx , & previous_resolve, & resolve, & mut registry) ?;
158
159
if opts. dry_run {
159
160
opts. gctx
160
161
. shell ( )
@@ -165,30 +166,177 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
165
166
Ok ( ( ) )
166
167
}
167
168
168
- fn print_lockfile_update (
169
+ pub fn print_lockfile_changes (
170
+ gctx : & GlobalContext ,
171
+ previous_resolve : Option < & Resolve > ,
172
+ resolve : & Resolve ,
173
+ registry : & mut PackageRegistry < ' _ > ,
174
+ ) -> CargoResult < ( ) > {
175
+ if let Some ( previous_resolve) = previous_resolve {
176
+ print_lockfile_sync ( gctx, previous_resolve, resolve, registry)
177
+ } else {
178
+ print_lockfile_generation ( gctx, resolve, registry)
179
+ }
180
+ }
181
+
182
+ fn print_lockfile_generation (
183
+ gctx : & GlobalContext ,
184
+ resolve : & Resolve ,
185
+ registry : & mut PackageRegistry < ' _ > ,
186
+ ) -> CargoResult < ( ) > {
187
+ let mut shell = gctx. shell ( ) ;
188
+
189
+ let diff = PackageDiff :: new ( & resolve) ;
190
+ let num_pkgs: usize = diff. iter ( ) . map ( |d| d. added . len ( ) ) . sum ( ) ;
191
+ if num_pkgs <= 1 {
192
+ // just ourself, nothing worth reporting
193
+ return Ok ( ( ) ) ;
194
+ }
195
+ shell. status ( "Locking" , format ! ( "{num_pkgs} packages" ) ) ?;
196
+
197
+ for diff in diff {
198
+ fn format_latest ( version : semver:: Version ) -> String {
199
+ let warn = style:: WARN ;
200
+ format ! ( " {warn}(latest: v{version}){warn:#}" )
201
+ }
202
+ let possibilities = if let Some ( query) = diff. alternatives_query ( ) {
203
+ loop {
204
+ match registry. query_vec ( & query, QueryKind :: Exact ) {
205
+ std:: task:: Poll :: Ready ( res) => {
206
+ break res?;
207
+ }
208
+ std:: task:: Poll :: Pending => registry. block_until_ready ( ) ?,
209
+ }
210
+ }
211
+ } else {
212
+ vec ! [ ]
213
+ } ;
214
+
215
+ for package in diff. added . iter ( ) {
216
+ let latest = if !possibilities. is_empty ( ) {
217
+ possibilities
218
+ . iter ( )
219
+ . map ( |s| s. as_summary ( ) )
220
+ . filter ( |s| is_latest ( s. version ( ) , package. version ( ) ) )
221
+ . map ( |s| s. version ( ) . clone ( ) )
222
+ . max ( )
223
+ . map ( format_latest)
224
+ } else {
225
+ None
226
+ } ;
227
+
228
+ if let Some ( latest) = latest {
229
+ shell. status_with_color ( "Adding" , format ! ( "{package}{latest}" ) , & style:: NOTE ) ?;
230
+ }
231
+ }
232
+ }
233
+
234
+ Ok ( ( ) )
235
+ }
236
+
237
+ fn print_lockfile_sync (
169
238
gctx : & GlobalContext ,
170
239
previous_resolve : & Resolve ,
171
240
resolve : & Resolve ,
172
241
registry : & mut PackageRegistry < ' _ > ,
173
242
) -> CargoResult < ( ) > {
174
- // Summarize what is changing for the user.
175
- let print_change = |status : & str , msg : String , color : & Style | {
176
- gctx. shell ( ) . status_with_color ( status, msg, color)
177
- } ;
243
+ let mut shell = gctx. shell ( ) ;
244
+
245
+ let diff = PackageDiff :: diff ( & previous_resolve, & resolve) ;
246
+ let num_pkgs: usize = diff. iter ( ) . map ( |d| d. added . len ( ) ) . sum ( ) ;
247
+ if num_pkgs == 0 {
248
+ return Ok ( ( ) ) ;
249
+ }
250
+ let plural = if num_pkgs == 1 { "" } else { "s" } ;
251
+ shell. status ( "Locking" , format ! ( "{num_pkgs} package{plural}" ) ) ?;
252
+
253
+ for diff in diff {
254
+ fn format_latest ( version : semver:: Version ) -> String {
255
+ let warn = style:: WARN ;
256
+ format ! ( " {warn}(latest: v{version}){warn:#}" )
257
+ }
258
+ let possibilities = if let Some ( query) = diff. alternatives_query ( ) {
259
+ loop {
260
+ match registry. query_vec ( & query, QueryKind :: Exact ) {
261
+ std:: task:: Poll :: Ready ( res) => {
262
+ break res?;
263
+ }
264
+ std:: task:: Poll :: Pending => registry. block_until_ready ( ) ?,
265
+ }
266
+ }
267
+ } else {
268
+ vec ! [ ]
269
+ } ;
270
+
271
+ if let Some ( ( removed, added) ) = diff. change ( ) {
272
+ let latest = if !possibilities. is_empty ( ) {
273
+ possibilities
274
+ . iter ( )
275
+ . map ( |s| s. as_summary ( ) )
276
+ . filter ( |s| is_latest ( s. version ( ) , added. version ( ) ) )
277
+ . map ( |s| s. version ( ) . clone ( ) )
278
+ . max ( )
279
+ . map ( format_latest)
280
+ } else {
281
+ None
282
+ }
283
+ . unwrap_or_default ( ) ;
284
+
285
+ let msg = if removed. source_id ( ) . is_git ( ) {
286
+ format ! (
287
+ "{removed} -> #{}" ,
288
+ & added. source_id( ) . precise_git_fragment( ) . unwrap( ) [ ..8 ] ,
289
+ )
290
+ } else {
291
+ format ! ( "{removed} -> v{}{latest}" , added. version( ) )
292
+ } ;
293
+
294
+ // If versions differ only in build metadata, we call it an "update"
295
+ // regardless of whether the build metadata has gone up or down.
296
+ // This metadata is often stuff like git commit hashes, which are
297
+ // not meaningfully ordered.
298
+ if removed. version ( ) . cmp_precedence ( added. version ( ) ) == Ordering :: Greater {
299
+ shell. status_with_color ( "Downgrading" , msg, & style:: WARN ) ?;
300
+ } else {
301
+ shell. status_with_color ( "Updating" , msg, & style:: GOOD ) ?;
302
+ }
303
+ } else {
304
+ for package in diff. added . iter ( ) {
305
+ let latest = if !possibilities. is_empty ( ) {
306
+ possibilities
307
+ . iter ( )
308
+ . map ( |s| s. as_summary ( ) )
309
+ . filter ( |s| is_latest ( s. version ( ) , package. version ( ) ) )
310
+ . map ( |s| s. version ( ) . clone ( ) )
311
+ . max ( )
312
+ . map ( format_latest)
313
+ } else {
314
+ None
315
+ }
316
+ . unwrap_or_default ( ) ;
317
+
318
+ shell. status_with_color ( "Adding" , format ! ( "{package}{latest}" ) , & style:: NOTE ) ?;
319
+ }
320
+ }
321
+ }
322
+
323
+ Ok ( ( ) )
324
+ }
325
+
326
+ pub fn print_lockfile_updates (
327
+ gctx : & GlobalContext ,
328
+ previous_resolve : & Resolve ,
329
+ resolve : & Resolve ,
330
+ registry : & mut PackageRegistry < ' _ > ,
331
+ ) -> CargoResult < ( ) > {
332
+ let mut shell = gctx. shell ( ) ;
333
+
178
334
let mut unchanged_behind = 0 ;
179
335
for diff in PackageDiff :: diff ( & previous_resolve, & resolve) {
180
336
fn format_latest ( version : semver:: Version ) -> String {
181
337
let warn = style:: WARN ;
182
338
format ! ( " {warn}(latest: v{version}){warn:#}" )
183
339
}
184
- fn is_latest ( candidate : & semver:: Version , current : & semver:: Version ) -> bool {
185
- current < candidate
186
- // Only match pre-release if major.minor.patch are the same
187
- && ( candidate. pre . is_empty ( )
188
- || ( candidate. major == current. major
189
- && candidate. minor == current. minor
190
- && candidate. patch == current. patch ) )
191
- }
192
340
let possibilities = if let Some ( query) = diff. alternatives_query ( ) {
193
341
loop {
194
342
match registry. query_vec ( & query, QueryKind :: Exact ) {
@@ -230,13 +378,13 @@ fn print_lockfile_update(
230
378
// This metadata is often stuff like git commit hashes, which are
231
379
// not meaningfully ordered.
232
380
if removed. version ( ) . cmp_precedence ( added. version ( ) ) == Ordering :: Greater {
233
- print_change ( "Downgrading" , msg, & style:: WARN ) ?;
381
+ shell . status_with_color ( "Downgrading" , msg, & style:: WARN ) ?;
234
382
} else {
235
- print_change ( "Updating" , msg, & style:: GOOD ) ?;
383
+ shell . status_with_color ( "Updating" , msg, & style:: GOOD ) ?;
236
384
}
237
385
} else {
238
386
for package in diff. removed . iter ( ) {
239
- print_change ( "Removing" , format ! ( "{package}" ) , & style:: ERROR ) ?;
387
+ shell . status_with_color ( "Removing" , format ! ( "{package}" ) , & style:: ERROR ) ?;
240
388
}
241
389
for package in diff. added . iter ( ) {
242
390
let latest = if !possibilities. is_empty ( ) {
@@ -252,7 +400,7 @@ fn print_lockfile_update(
252
400
}
253
401
. unwrap_or_default ( ) ;
254
402
255
- print_change ( "Adding" , format ! ( "{package}{latest}" ) , & style:: NOTE ) ?;
403
+ shell . status_with_color ( "Adding" , format ! ( "{package}{latest}" ) , & style:: NOTE ) ?;
256
404
}
257
405
}
258
406
for package in & diff. unchanged {
@@ -270,8 +418,8 @@ fn print_lockfile_update(
270
418
271
419
if let Some ( latest) = latest {
272
420
unchanged_behind += 1 ;
273
- if gctx . shell ( ) . verbosity ( ) == Verbosity :: Verbose {
274
- gctx . shell ( ) . status_with_color (
421
+ if shell. verbosity ( ) == Verbosity :: Verbose {
422
+ shell. status_with_color (
275
423
"Unchanged" ,
276
424
format ! ( "{package}{latest}" ) ,
277
425
& anstyle:: Style :: new ( ) . bold ( ) ,
@@ -280,13 +428,13 @@ fn print_lockfile_update(
280
428
}
281
429
}
282
430
}
283
- if gctx . shell ( ) . verbosity ( ) == Verbosity :: Verbose {
284
- gctx . shell ( ) . note (
431
+ if shell. verbosity ( ) == Verbosity :: Verbose {
432
+ shell. note (
285
433
"to see how you depend on a package, run `cargo tree --invert --package <dep>@<ver>`" ,
286
434
) ?;
287
435
} else {
288
436
if 0 < unchanged_behind {
289
- gctx . shell ( ) . note ( format ! (
437
+ shell. note ( format ! (
290
438
"pass `--verbose` to see {unchanged_behind} unchanged dependencies behind latest"
291
439
) ) ?;
292
440
}
@@ -295,6 +443,15 @@ fn print_lockfile_update(
295
443
Ok ( ( ) )
296
444
}
297
445
446
+ fn is_latest ( candidate : & semver:: Version , current : & semver:: Version ) -> bool {
447
+ current < candidate
448
+ // Only match pre-release if major.minor.patch are the same
449
+ && ( candidate. pre . is_empty ( )
450
+ || ( candidate. major == current. major
451
+ && candidate. minor == current. minor
452
+ && candidate. patch == current. patch ) )
453
+ }
454
+
298
455
fn fill_with_deps < ' a > (
299
456
resolve : & ' a Resolve ,
300
457
dep : PackageId ,
@@ -319,11 +476,21 @@ pub struct PackageDiff {
319
476
}
320
477
321
478
impl PackageDiff {
322
- pub fn diff ( previous_resolve : & Resolve , resolve : & Resolve ) -> Vec < Self > {
323
- fn key ( dep : PackageId ) -> ( & ' static str , SourceId ) {
324
- ( dep. name ( ) . as_str ( ) , dep. source_id ( ) )
479
+ pub fn new ( resolve : & Resolve ) -> Vec < Self > {
480
+ let mut changes = BTreeMap :: new ( ) ;
481
+ let empty = Self :: default ( ) ;
482
+ for dep in resolve. iter ( ) {
483
+ changes
484
+ . entry ( Self :: key ( dep) )
485
+ . or_insert_with ( || empty. clone ( ) )
486
+ . added
487
+ . push ( dep) ;
325
488
}
326
489
490
+ changes. into_iter ( ) . map ( |( _, v) | v) . collect ( )
491
+ }
492
+
493
+ pub fn diff ( previous_resolve : & Resolve , resolve : & Resolve ) -> Vec < Self > {
327
494
fn vec_subset ( a : & [ PackageId ] , b : & [ PackageId ] ) -> Vec < PackageId > {
328
495
a. iter ( ) . filter ( |a| !contains_id ( b, a) ) . cloned ( ) . collect ( )
329
496
}
@@ -364,14 +531,14 @@ impl PackageDiff {
364
531
let empty = Self :: default ( ) ;
365
532
for dep in previous_resolve. iter ( ) {
366
533
changes
367
- . entry ( key ( dep) )
534
+ . entry ( Self :: key ( dep) )
368
535
. or_insert_with ( || empty. clone ( ) )
369
536
. removed
370
537
. push ( dep) ;
371
538
}
372
539
for dep in resolve. iter ( ) {
373
540
changes
374
- . entry ( key ( dep) )
541
+ . entry ( Self :: key ( dep) )
375
542
. or_insert_with ( || empty. clone ( ) )
376
543
. added
377
544
. push ( dep) ;
@@ -397,6 +564,10 @@ impl PackageDiff {
397
564
changes. into_iter ( ) . map ( |( _, v) | v) . collect ( )
398
565
}
399
566
567
+ fn key ( dep : PackageId ) -> ( & ' static str , SourceId ) {
568
+ ( dep. name ( ) . as_str ( ) , dep. source_id ( ) )
569
+ }
570
+
400
571
/// Guess if a package upgraded/downgraded
401
572
///
402
573
/// All `PackageDiff` knows is that entries were added/removed within [`Resolve`].
0 commit comments