@@ -25,17 +25,21 @@ pub struct UpdateOptions<'a> {
25
25
26
26
pub fn generate_lockfile ( ws : & Workspace < ' _ > ) -> CargoResult < ( ) > {
27
27
let mut registry = PackageRegistry :: new ( ws. gctx ( ) ) ?;
28
+ let previous_resolve = None ;
28
29
let mut resolve = ops:: resolve_with_previous (
29
30
& mut registry,
30
31
ws,
31
32
& CliFeatures :: new_all ( true ) ,
32
33
HasDevUnits :: Yes ,
33
- None ,
34
+ previous_resolve ,
34
35
None ,
35
36
& [ ] ,
36
37
true ,
37
38
) ?;
38
- ops:: write_pkg_lockfile ( ws, & mut resolve) ?;
39
+ let changed = ops:: write_pkg_lockfile ( ws, & mut resolve) ?;
40
+ if changed {
41
+ print_lockfile_changes ( ws. gctx ( ) , previous_resolve, & resolve, & mut registry) ?;
42
+ }
39
43
Ok ( ( ) )
40
44
}
41
45
@@ -171,7 +175,157 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
171
175
Ok ( ( ) )
172
176
}
173
177
174
- fn print_lockfile_updates (
178
+ pub fn print_lockfile_changes (
179
+ gctx : & GlobalContext ,
180
+ previous_resolve : Option < & Resolve > ,
181
+ resolve : & Resolve ,
182
+ registry : & mut PackageRegistry < ' _ > ,
183
+ ) -> CargoResult < ( ) > {
184
+ if let Some ( previous_resolve) = previous_resolve {
185
+ print_lockfile_sync ( gctx, previous_resolve, resolve, registry)
186
+ } else {
187
+ print_lockfile_generation ( gctx, resolve, registry)
188
+ }
189
+ }
190
+
191
+ fn print_lockfile_generation (
192
+ gctx : & GlobalContext ,
193
+ resolve : & Resolve ,
194
+ registry : & mut PackageRegistry < ' _ > ,
195
+ ) -> CargoResult < ( ) > {
196
+ let mut shell = gctx. shell ( ) ;
197
+
198
+ let num_pkgs = resolve. iter ( ) . count ( ) ;
199
+ if num_pkgs == 1 {
200
+ // just ourself, nothing worth reporting
201
+ return Ok ( ( ) ) ;
202
+ }
203
+ shell. status ( "Locking" , format ! ( "{num_pkgs} packages" ) ) ?;
204
+
205
+ for diff in PackageDiff :: new ( & resolve) {
206
+ fn format_latest ( version : semver:: Version ) -> String {
207
+ let warn = style:: WARN ;
208
+ format ! ( " {warn}(latest: v{version}){warn:#}" )
209
+ }
210
+ let possibilities = if let Some ( query) = diff. alternatives_query ( ) {
211
+ loop {
212
+ match registry. query_vec ( & query, QueryKind :: Exact ) {
213
+ std:: task:: Poll :: Ready ( res) => {
214
+ break res?;
215
+ }
216
+ std:: task:: Poll :: Pending => registry. block_until_ready ( ) ?,
217
+ }
218
+ }
219
+ } else {
220
+ vec ! [ ]
221
+ } ;
222
+
223
+ for package in diff. added . iter ( ) {
224
+ let latest = if !possibilities. is_empty ( ) {
225
+ possibilities
226
+ . iter ( )
227
+ . map ( |s| s. as_summary ( ) )
228
+ . filter ( |s| is_latest ( s. version ( ) , package. version ( ) ) )
229
+ . map ( |s| s. version ( ) . clone ( ) )
230
+ . max ( )
231
+ . map ( format_latest)
232
+ } else {
233
+ None
234
+ } ;
235
+
236
+ if let Some ( latest) = latest {
237
+ shell. status_with_color ( "Adding" , format ! ( "{package}{latest}" ) , & style:: NOTE ) ?;
238
+ }
239
+ }
240
+ }
241
+
242
+ Ok ( ( ) )
243
+ }
244
+
245
+ fn print_lockfile_sync (
246
+ gctx : & GlobalContext ,
247
+ previous_resolve : & Resolve ,
248
+ resolve : & Resolve ,
249
+ registry : & mut PackageRegistry < ' _ > ,
250
+ ) -> CargoResult < ( ) > {
251
+ let mut shell = gctx. shell ( ) ;
252
+
253
+ shell. status ( "Locking" , "packages" ) ?;
254
+
255
+ for diff in PackageDiff :: diff ( & previous_resolve, & resolve) {
256
+ fn format_latest ( version : semver:: Version ) -> String {
257
+ let warn = style:: WARN ;
258
+ format ! ( " {warn}(latest: v{version}){warn:#}" )
259
+ }
260
+ let possibilities = if let Some ( query) = diff. alternatives_query ( ) {
261
+ loop {
262
+ match registry. query_vec ( & query, QueryKind :: Exact ) {
263
+ std:: task:: Poll :: Ready ( res) => {
264
+ break res?;
265
+ }
266
+ std:: task:: Poll :: Pending => registry. block_until_ready ( ) ?,
267
+ }
268
+ }
269
+ } else {
270
+ vec ! [ ]
271
+ } ;
272
+
273
+ if let Some ( ( removed, added) ) = diff. change ( ) {
274
+ let latest = if !possibilities. is_empty ( ) {
275
+ possibilities
276
+ . iter ( )
277
+ . map ( |s| s. as_summary ( ) )
278
+ . filter ( |s| is_latest ( s. version ( ) , added. version ( ) ) )
279
+ . map ( |s| s. version ( ) . clone ( ) )
280
+ . max ( )
281
+ . map ( format_latest)
282
+ } else {
283
+ None
284
+ }
285
+ . unwrap_or_default ( ) ;
286
+
287
+ let msg = if removed. source_id ( ) . is_git ( ) {
288
+ format ! (
289
+ "{removed} -> #{}" ,
290
+ & added. source_id( ) . precise_git_fragment( ) . unwrap( ) [ ..8 ] ,
291
+ )
292
+ } else {
293
+ format ! ( "{removed} -> v{}{latest}" , added. version( ) )
294
+ } ;
295
+
296
+ // If versions differ only in build metadata, we call it an "update"
297
+ // regardless of whether the build metadata has gone up or down.
298
+ // This metadata is often stuff like git commit hashes, which are
299
+ // not meaningfully ordered.
300
+ if removed. version ( ) . cmp_precedence ( added. version ( ) ) == Ordering :: Greater {
301
+ shell. status_with_color ( "Downgrading" , msg, & style:: WARN ) ?;
302
+ } else {
303
+ shell. status_with_color ( "Updating" , msg, & style:: GOOD ) ?;
304
+ }
305
+ } else {
306
+ for package in diff. added . iter ( ) {
307
+ let latest = if !possibilities. is_empty ( ) {
308
+ possibilities
309
+ . iter ( )
310
+ . map ( |s| s. as_summary ( ) )
311
+ . filter ( |s| is_latest ( s. version ( ) , package. version ( ) ) )
312
+ . map ( |s| s. version ( ) . clone ( ) )
313
+ . max ( )
314
+ . map ( format_latest)
315
+ } else {
316
+ None
317
+ }
318
+ . unwrap_or_default ( ) ;
319
+
320
+ shell. status_with_color ( "Adding" , format ! ( "{package}{latest}" ) , & style:: NOTE ) ?;
321
+ }
322
+ }
323
+ }
324
+
325
+ Ok ( ( ) )
326
+ }
327
+
328
+ pub fn print_lockfile_updates (
175
329
gctx : & GlobalContext ,
176
330
previous_resolve : & Resolve ,
177
331
resolve : & Resolve ,
@@ -324,11 +478,21 @@ pub struct PackageDiff {
324
478
}
325
479
326
480
impl PackageDiff {
327
- pub fn diff ( previous_resolve : & Resolve , resolve : & Resolve ) -> Vec < Self > {
328
- fn key ( dep : PackageId ) -> ( & ' static str , SourceId ) {
329
- ( dep. name ( ) . as_str ( ) , dep. source_id ( ) )
481
+ pub fn new ( resolve : & Resolve ) -> Vec < Self > {
482
+ let mut changes = BTreeMap :: new ( ) ;
483
+ let empty = Self :: default ( ) ;
484
+ for dep in resolve. iter ( ) {
485
+ changes
486
+ . entry ( Self :: key ( dep) )
487
+ . or_insert_with ( || empty. clone ( ) )
488
+ . added
489
+ . push ( dep) ;
330
490
}
331
491
492
+ changes. into_iter ( ) . map ( |( _, v) | v) . collect ( )
493
+ }
494
+
495
+ pub fn diff ( previous_resolve : & Resolve , resolve : & Resolve ) -> Vec < Self > {
332
496
fn vec_subset ( a : & [ PackageId ] , b : & [ PackageId ] ) -> Vec < PackageId > {
333
497
a. iter ( ) . filter ( |a| !contains_id ( b, a) ) . cloned ( ) . collect ( )
334
498
}
@@ -369,14 +533,14 @@ impl PackageDiff {
369
533
let empty = Self :: default ( ) ;
370
534
for dep in previous_resolve. iter ( ) {
371
535
changes
372
- . entry ( key ( dep) )
536
+ . entry ( Self :: key ( dep) )
373
537
. or_insert_with ( || empty. clone ( ) )
374
538
. removed
375
539
. push ( dep) ;
376
540
}
377
541
for dep in resolve. iter ( ) {
378
542
changes
379
- . entry ( key ( dep) )
543
+ . entry ( Self :: key ( dep) )
380
544
. or_insert_with ( || empty. clone ( ) )
381
545
. added
382
546
. push ( dep) ;
@@ -402,6 +566,10 @@ impl PackageDiff {
402
566
changes. into_iter ( ) . map ( |( _, v) | v) . collect ( )
403
567
}
404
568
569
+ fn key ( dep : PackageId ) -> ( & ' static str , SourceId ) {
570
+ ( dep. name ( ) . as_str ( ) , dep. source_id ( ) )
571
+ }
572
+
405
573
/// Guess if a package upgraded/downgraded
406
574
///
407
575
/// All `PackageDiff` knows is that entries were added/removed within [`Resolve`].
0 commit comments