@@ -10,7 +10,6 @@ use crate::util::cache_lock::CacheLockMode;
1010use crate :: util:: context:: GlobalContext ;
1111use crate :: util:: style;
1212use crate :: util:: CargoResult ;
13- use anstyle:: Style ;
1413use std:: cmp:: Ordering ;
1514use std:: collections:: { BTreeMap , HashSet } ;
1615use tracing:: debug;
@@ -26,17 +25,19 @@ pub struct UpdateOptions<'a> {
2625
2726pub fn generate_lockfile ( ws : & Workspace < ' _ > ) -> CargoResult < ( ) > {
2827 let mut registry = PackageRegistry :: new ( ws. gctx ( ) ) ?;
28+ let previous_resolve = None ;
2929 let mut resolve = ops:: resolve_with_previous (
3030 & mut registry,
3131 ws,
3232 & CliFeatures :: new_all ( true ) ,
3333 HasDevUnits :: Yes ,
34- None ,
34+ previous_resolve ,
3535 None ,
3636 & [ ] ,
3737 true ,
3838 ) ?;
3939 ops:: write_pkg_lockfile ( ws, & mut resolve) ?;
40+ print_lockfile_changes ( ws. gctx ( ) , previous_resolve, & resolve, & mut registry) ?;
4041 Ok ( ( ) )
4142}
4243
@@ -154,7 +155,7 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
154155 true ,
155156 ) ?;
156157
157- print_lockfile_update ( opts. gctx , & previous_resolve, & resolve, & mut registry) ?;
158+ print_lockfile_updates ( opts. gctx , & previous_resolve, & resolve, & mut registry) ?;
158159 if opts. dry_run {
159160 opts. gctx
160161 . shell ( )
@@ -165,30 +166,177 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
165166 Ok ( ( ) )
166167}
167168
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 (
169238 gctx : & GlobalContext ,
170239 previous_resolve : & Resolve ,
171240 resolve : & Resolve ,
172241 registry : & mut PackageRegistry < ' _ > ,
173242) -> 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+
178334 let mut unchanged_behind = 0 ;
179335 for diff in PackageDiff :: diff ( & previous_resolve, & resolve) {
180336 fn format_latest ( version : semver:: Version ) -> String {
181337 let warn = style:: WARN ;
182338 format ! ( " {warn}(latest: v{version}){warn:#}" )
183339 }
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- }
192340 let possibilities = if let Some ( query) = diff. alternatives_query ( ) {
193341 loop {
194342 match registry. query_vec ( & query, QueryKind :: Exact ) {
@@ -230,13 +378,13 @@ fn print_lockfile_update(
230378 // This metadata is often stuff like git commit hashes, which are
231379 // not meaningfully ordered.
232380 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 ) ?;
234382 } else {
235- print_change ( "Updating" , msg, & style:: GOOD ) ?;
383+ shell . status_with_color ( "Updating" , msg, & style:: GOOD ) ?;
236384 }
237385 } else {
238386 for package in diff. removed . iter ( ) {
239- print_change ( "Removing" , format ! ( "{package}" ) , & style:: ERROR ) ?;
387+ shell . status_with_color ( "Removing" , format ! ( "{package}" ) , & style:: ERROR ) ?;
240388 }
241389 for package in diff. added . iter ( ) {
242390 let latest = if !possibilities. is_empty ( ) {
@@ -252,7 +400,7 @@ fn print_lockfile_update(
252400 }
253401 . unwrap_or_default ( ) ;
254402
255- print_change ( "Adding" , format ! ( "{package}{latest}" ) , & style:: NOTE ) ?;
403+ shell . status_with_color ( "Adding" , format ! ( "{package}{latest}" ) , & style:: NOTE ) ?;
256404 }
257405 }
258406 for package in & diff. unchanged {
@@ -270,8 +418,8 @@ fn print_lockfile_update(
270418
271419 if let Some ( latest) = latest {
272420 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 (
275423 "Unchanged" ,
276424 format ! ( "{package}{latest}" ) ,
277425 & anstyle:: Style :: new ( ) . bold ( ) ,
@@ -280,13 +428,13 @@ fn print_lockfile_update(
280428 }
281429 }
282430 }
283- if gctx . shell ( ) . verbosity ( ) == Verbosity :: Verbose {
284- gctx . shell ( ) . note (
431+ if shell. verbosity ( ) == Verbosity :: Verbose {
432+ shell. note (
285433 "to see how you depend on a package, run `cargo tree --invert --package <dep>@<ver>`" ,
286434 ) ?;
287435 } else {
288436 if 0 < unchanged_behind {
289- gctx . shell ( ) . note ( format ! (
437+ shell. note ( format ! (
290438 "pass `--verbose` to see {unchanged_behind} unchanged dependencies behind latest"
291439 ) ) ?;
292440 }
@@ -295,6 +443,15 @@ fn print_lockfile_update(
295443 Ok ( ( ) )
296444}
297445
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+
298455fn fill_with_deps < ' a > (
299456 resolve : & ' a Resolve ,
300457 dep : PackageId ,
@@ -319,11 +476,21 @@ pub struct PackageDiff {
319476}
320477
321478impl 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) ;
325488 }
326489
490+ changes. into_iter ( ) . map ( |( _, v) | v) . collect ( )
491+ }
492+
493+ pub fn diff ( previous_resolve : & Resolve , resolve : & Resolve ) -> Vec < Self > {
327494 fn vec_subset ( a : & [ PackageId ] , b : & [ PackageId ] ) -> Vec < PackageId > {
328495 a. iter ( ) . filter ( |a| !contains_id ( b, a) ) . cloned ( ) . collect ( )
329496 }
@@ -364,14 +531,14 @@ impl PackageDiff {
364531 let empty = Self :: default ( ) ;
365532 for dep in previous_resolve. iter ( ) {
366533 changes
367- . entry ( key ( dep) )
534+ . entry ( Self :: key ( dep) )
368535 . or_insert_with ( || empty. clone ( ) )
369536 . removed
370537 . push ( dep) ;
371538 }
372539 for dep in resolve. iter ( ) {
373540 changes
374- . entry ( key ( dep) )
541+ . entry ( Self :: key ( dep) )
375542 . or_insert_with ( || empty. clone ( ) )
376543 . added
377544 . push ( dep) ;
@@ -397,6 +564,10 @@ impl PackageDiff {
397564 changes. into_iter ( ) . map ( |( _, v) | v) . collect ( )
398565 }
399566
567+ fn key ( dep : PackageId ) -> ( & ' static str , SourceId ) {
568+ ( dep. name ( ) . as_str ( ) , dep. source_id ( ) )
569+ }
570+
400571 /// Guess if a package upgraded/downgraded
401572 ///
402573 /// All `PackageDiff` knows is that entries were added/removed within [`Resolve`].
0 commit comments