@@ -25,17 +25,21 @@ pub struct UpdateOptions<'a> {
2525
2626pub fn generate_lockfile ( ws : & Workspace < ' _ > ) -> CargoResult < ( ) > {
2727 let mut registry = PackageRegistry :: new ( ws. gctx ( ) ) ?;
28+ let previous_resolve = None ;
2829 let mut resolve = ops:: resolve_with_previous (
2930 & mut registry,
3031 ws,
3132 & CliFeatures :: new_all ( true ) ,
3233 HasDevUnits :: Yes ,
33- None ,
34+ previous_resolve ,
3435 None ,
3536 & [ ] ,
3637 true ,
3738 ) ?;
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+ }
3943 Ok ( ( ) )
4044}
4145
@@ -171,7 +175,157 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
171175 Ok ( ( ) )
172176}
173177
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 (
175329 gctx : & GlobalContext ,
176330 previous_resolve : & Resolve ,
177331 resolve : & Resolve ,
@@ -324,11 +478,21 @@ pub struct PackageDiff {
324478}
325479
326480impl 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) ;
330490 }
331491
492+ changes. into_iter ( ) . map ( |( _, v) | v) . collect ( )
493+ }
494+
495+ pub fn diff ( previous_resolve : & Resolve , resolve : & Resolve ) -> Vec < Self > {
332496 fn vec_subset ( a : & [ PackageId ] , b : & [ PackageId ] ) -> Vec < PackageId > {
333497 a. iter ( ) . filter ( |a| !contains_id ( b, a) ) . cloned ( ) . collect ( )
334498 }
@@ -369,14 +533,14 @@ impl PackageDiff {
369533 let empty = Self :: default ( ) ;
370534 for dep in previous_resolve. iter ( ) {
371535 changes
372- . entry ( key ( dep) )
536+ . entry ( Self :: key ( dep) )
373537 . or_insert_with ( || empty. clone ( ) )
374538 . removed
375539 . push ( dep) ;
376540 }
377541 for dep in resolve. iter ( ) {
378542 changes
379- . entry ( key ( dep) )
543+ . entry ( Self :: key ( dep) )
380544 . or_insert_with ( || empty. clone ( ) )
381545 . added
382546 . push ( dep) ;
@@ -402,6 +566,10 @@ impl PackageDiff {
402566 changes. into_iter ( ) . map ( |( _, v) | v) . collect ( )
403567 }
404568
569+ fn key ( dep : PackageId ) -> ( & ' static str , SourceId ) {
570+ ( dep. name ( ) . as_str ( ) , dep. source_id ( ) )
571+ }
572+
405573 /// Guess if a package upgraded/downgraded
406574 ///
407575 /// All `PackageDiff` knows is that entries were added/removed within [`Resolve`].
0 commit comments