@@ -17,6 +17,7 @@ use crate::util::{style, OptVersionReq};
1717use crate :: util:: { CargoResult , VersionExt } ;
1818use anyhow:: Context as _;
1919use cargo_util_schemas:: core:: PartialVersion ;
20+ use indexmap:: IndexMap ;
2021use itertools:: Itertools ;
2122use semver:: { Op , Version , VersionReq } ;
2223use std:: cmp:: Ordering ;
@@ -491,16 +492,16 @@ fn print_lockfile_generation(
491492 resolve : & Resolve ,
492493 registry : & mut PackageRegistry < ' _ > ,
493494) -> CargoResult < ( ) > {
494- let diff : Vec < _ > = PackageDiff :: new ( & resolve) . collect ( ) ;
495- let num_pkgs: usize = diff . iter ( ) . map ( |d| d . added . len ( ) ) . sum ( ) ;
495+ let changes = PackageChange :: new ( resolve) ;
496+ let num_pkgs: usize = changes . iter ( ) . filter ( |change| change . kind . is_new ( ) ) . count ( ) ;
496497 if num_pkgs <= 1 {
497498 // just ourself, nothing worth reporting
498499 return Ok ( ( ) ) ;
499500 }
500501 status_locking ( ws, num_pkgs) ?;
501502
502- for diff in diff {
503- let possibilities = if let Some ( query) = diff . alternatives_query ( ) {
503+ for change in changes {
504+ let possibilities = if let Some ( query) = change . alternatives_query ( ) {
504505 loop {
505506 match registry. query_vec ( & query, QueryKind :: Exact ) {
506507 std:: task:: Poll :: Ready ( res) => {
@@ -513,12 +514,14 @@ fn print_lockfile_generation(
513514 vec ! [ ]
514515 } ;
515516
516- for package_id in diff. added {
517+ {
518+ let package_id = change. package_id ;
517519 let required_rust_version = report_required_rust_version ( ws, resolve, package_id) ;
518520 let latest = report_latest ( & possibilities, package_id) ;
519521 let note = required_rust_version. or ( latest) ;
520522
521523 if let Some ( note) = note {
524+ assert_eq ! ( change. kind, PackageChangeKind :: Added ) ;
522525 ws. gctx ( ) . shell ( ) . status_with_color (
523526 "Adding" ,
524527 format ! ( "{package_id}{note}" ) ,
@@ -537,15 +540,15 @@ fn print_lockfile_sync(
537540 resolve : & Resolve ,
538541 registry : & mut PackageRegistry < ' _ > ,
539542) -> CargoResult < ( ) > {
540- let diff : Vec < _ > = PackageDiff :: diff ( & previous_resolve, & resolve) . collect ( ) ;
541- let num_pkgs: usize = diff . iter ( ) . map ( |d| d . added . len ( ) ) . sum ( ) ;
543+ let changes = PackageChange :: diff ( previous_resolve, resolve) ;
544+ let num_pkgs: usize = changes . iter ( ) . filter ( |change| change . kind . is_new ( ) ) . count ( ) ;
542545 if num_pkgs == 0 {
543546 return Ok ( ( ) ) ;
544547 }
545548 status_locking ( ws, num_pkgs) ?;
546549
547- for diff in diff {
548- let possibilities = if let Some ( query) = diff . alternatives_query ( ) {
550+ for change in changes {
551+ let possibilities = if let Some ( query) = change . alternatives_query ( ) {
549552 loop {
550553 match registry. query_vec ( & query, QueryKind :: Exact ) {
551554 std:: task:: Poll :: Ready ( res) => {
@@ -558,7 +561,8 @@ fn print_lockfile_sync(
558561 vec ! [ ]
559562 } ;
560563
561- if let Some ( ( previous_id, package_id) ) = diff. change ( ) {
564+ let package_id = change. package_id ;
565+ if let Some ( previous_id) = change. previous_id {
562566 let required_rust_version = report_required_rust_version ( ws, resolve, package_id) ;
563567 let latest = report_latest ( & possibilities, package_id) ;
564568 let note = required_rust_version. or ( latest) . unwrap_or_default ( ) ;
@@ -572,11 +576,7 @@ fn print_lockfile_sync(
572576 format ! ( "{previous_id} -> v{}{note}" , package_id. version( ) )
573577 } ;
574578
575- // If versions differ only in build metadata, we call it an "update"
576- // regardless of whether the build metadata has gone up or down.
577- // This metadata is often stuff like git commit hashes, which are
578- // not meaningfully ordered.
579- if previous_id. version ( ) . cmp_precedence ( package_id. version ( ) ) == Ordering :: Greater {
579+ if change. kind == PackageChangeKind :: Downgraded {
580580 ws. gctx ( )
581581 . shell ( )
582582 . status_with_color ( "Downgrading" , msg, & style:: WARN ) ?;
@@ -586,7 +586,7 @@ fn print_lockfile_sync(
586586 . status_with_color ( "Updating" , msg, & style:: GOOD ) ?;
587587 }
588588 } else {
589- for package_id in diff . added {
589+ if change . kind == PackageChangeKind :: Added {
590590 let required_rust_version = report_required_rust_version ( ws, resolve, package_id) ;
591591 let latest = report_latest ( & possibilities, package_id) ;
592592 let note = required_rust_version. or ( latest) . unwrap_or_default ( ) ;
@@ -610,15 +610,15 @@ fn print_lockfile_updates(
610610 precise : bool ,
611611 registry : & mut PackageRegistry < ' _ > ,
612612) -> CargoResult < ( ) > {
613- let diff : Vec < _ > = PackageDiff :: diff ( & previous_resolve, & resolve) . collect ( ) ;
614- let num_pkgs: usize = diff . iter ( ) . map ( |d| d . added . len ( ) ) . sum ( ) ;
613+ let changes = PackageChange :: diff ( previous_resolve, resolve) ;
614+ let num_pkgs: usize = changes . iter ( ) . filter ( |change| change . kind . is_new ( ) ) . count ( ) ;
615615 if !precise {
616616 status_locking ( ws, num_pkgs) ?;
617617 }
618618
619619 let mut unchanged_behind = 0 ;
620- for diff in diff {
621- let possibilities = if let Some ( query) = diff . alternatives_query ( ) {
620+ for change in changes {
621+ let possibilities = if let Some ( query) = change . alternatives_query ( ) {
622622 loop {
623623 match registry. query_vec ( & query, QueryKind :: Exact ) {
624624 std:: task:: Poll :: Ready ( res) => {
@@ -631,7 +631,8 @@ fn print_lockfile_updates(
631631 vec ! [ ]
632632 } ;
633633
634- if let Some ( ( previous_id, package_id) ) = diff. change ( ) {
634+ let package_id = change. package_id ;
635+ if let Some ( previous_id) = change. previous_id {
635636 let required_rust_version = report_required_rust_version ( ws, resolve, package_id) ;
636637 let latest = report_latest ( & possibilities, package_id) ;
637638 let note = required_rust_version. or ( latest) . unwrap_or_default ( ) ;
@@ -645,11 +646,7 @@ fn print_lockfile_updates(
645646 format ! ( "{previous_id} -> v{}{note}" , package_id. version( ) )
646647 } ;
647648
648- // If versions differ only in build metadata, we call it an "update"
649- // regardless of whether the build metadata has gone up or down.
650- // This metadata is often stuff like git commit hashes, which are
651- // not meaningfully ordered.
652- if previous_id. version ( ) . cmp_precedence ( package_id. version ( ) ) == Ordering :: Greater {
649+ if change. kind == PackageChangeKind :: Downgraded {
653650 ws. gctx ( )
654651 . shell ( )
655652 . status_with_color ( "Downgrading" , msg, & style:: WARN ) ?;
@@ -659,14 +656,13 @@ fn print_lockfile_updates(
659656 . status_with_color ( "Updating" , msg, & style:: GOOD ) ?;
660657 }
661658 } else {
662- for package_id in diff . removed {
659+ if change . kind == PackageChangeKind :: Removed {
663660 ws. gctx ( ) . shell ( ) . status_with_color (
664661 "Removing" ,
665662 format ! ( "{package_id}" ) ,
666663 & style:: ERROR ,
667664 ) ?;
668- }
669- for package_id in diff. added {
665+ } else if change. kind == PackageChangeKind :: Added {
670666 let required_rust_version = report_required_rust_version ( ws, resolve, package_id) ;
671667 let latest = report_latest ( & possibilities, package_id) ;
672668 let note = required_rust_version. or ( latest) . unwrap_or_default ( ) ;
@@ -678,7 +674,7 @@ fn print_lockfile_updates(
678674 ) ?;
679675 }
680676 }
681- for package_id in diff . unchanged {
677+ if change . kind == PackageChangeKind :: Unchanged {
682678 let required_rust_version = report_required_rust_version ( ws, resolve, package_id) ;
683679 let latest = report_latest ( & possibilities, package_id) ;
684680 let note = required_rust_version. as_deref ( ) . or ( latest. as_deref ( ) ) ;
@@ -818,6 +814,113 @@ fn fill_with_deps<'a>(
818814 }
819815}
820816
817+ #[ derive( Clone , Debug ) ]
818+ struct PackageChange {
819+ package_id : PackageId ,
820+ previous_id : Option < PackageId > ,
821+ kind : PackageChangeKind ,
822+ }
823+
824+ impl PackageChange {
825+ pub fn new ( resolve : & Resolve ) -> Vec < Self > {
826+ let diff = PackageDiff :: new ( resolve) ;
827+ Self :: with_diff ( diff)
828+ }
829+
830+ pub fn diff ( previous_resolve : & Resolve , resolve : & Resolve ) -> Vec < Self > {
831+ let diff = PackageDiff :: diff ( previous_resolve, resolve) ;
832+ Self :: with_diff ( diff)
833+ }
834+
835+ fn with_diff ( diff : impl Iterator < Item = PackageDiff > ) -> Vec < Self > {
836+ let mut changes = IndexMap :: new ( ) ;
837+ for diff in diff {
838+ if let Some ( ( previous_id, package_id) ) = diff. change ( ) {
839+ // If versions differ only in build metadata, we call it an "update"
840+ // regardless of whether the build metadata has gone up or down.
841+ // This metadata is often stuff like git commit hashes, which are
842+ // not meaningfully ordered.
843+ let kind = if previous_id. version ( ) . cmp_precedence ( package_id. version ( ) )
844+ == Ordering :: Greater
845+ {
846+ PackageChangeKind :: Downgraded
847+ } else {
848+ PackageChangeKind :: Upgraded
849+ } ;
850+ let change = Self {
851+ package_id,
852+ previous_id : Some ( previous_id) ,
853+ kind,
854+ } ;
855+ changes. insert ( change. package_id , change) ;
856+ } else {
857+ for package_id in diff. removed {
858+ let kind = PackageChangeKind :: Removed ;
859+ let change = Self {
860+ package_id,
861+ previous_id : None ,
862+ kind,
863+ } ;
864+ changes. insert ( change. package_id , change) ;
865+ }
866+ for package_id in diff. added {
867+ let kind = PackageChangeKind :: Added ;
868+ let change = Self {
869+ package_id,
870+ previous_id : None ,
871+ kind,
872+ } ;
873+ changes. insert ( change. package_id , change) ;
874+ }
875+ }
876+ for package_id in diff. unchanged {
877+ let kind = PackageChangeKind :: Unchanged ;
878+ let change = Self {
879+ package_id,
880+ previous_id : None ,
881+ kind,
882+ } ;
883+ changes. insert ( change. package_id , change) ;
884+ }
885+ }
886+
887+ changes. into_values ( ) . collect ( )
888+ }
889+
890+ /// For querying [`PackageRegistry`] for alternative versions to report to the user
891+ fn alternatives_query ( & self ) -> Option < crate :: core:: dependency:: Dependency > {
892+ if !self . package_id . source_id ( ) . is_registry ( ) {
893+ return None ;
894+ }
895+
896+ let query = crate :: core:: dependency:: Dependency :: parse (
897+ self . package_id . name ( ) ,
898+ None ,
899+ self . package_id . source_id ( ) ,
900+ )
901+ . expect ( "already a valid dependency" ) ;
902+ Some ( query)
903+ }
904+ }
905+
906+ #[ derive( Copy , Clone , Debug , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
907+ enum PackageChangeKind {
908+ Added ,
909+ Removed ,
910+ Upgraded ,
911+ Downgraded ,
912+ Unchanged ,
913+ }
914+
915+ impl PackageChangeKind {
916+ pub fn is_new ( & self ) -> bool {
917+ match self {
918+ Self :: Added | Self :: Upgraded | Self :: Downgraded => true ,
919+ Self :: Removed | Self :: Unchanged => false ,
920+ }
921+ }
922+ }
923+
821924/// All resolved versions of a package name within a [`SourceId`]
822925#[ derive( Default , Clone , Debug ) ]
823926pub struct PackageDiff {
@@ -931,25 +1034,4 @@ impl PackageDiff {
9311034 None
9321035 }
9331036 }
934-
935- /// For querying [`PackageRegistry`] for alternative versions to report to the user
936- pub fn alternatives_query ( & self ) -> Option < crate :: core:: dependency:: Dependency > {
937- let package_id = [
938- self . added . iter ( ) ,
939- self . unchanged . iter ( ) ,
940- self . removed . iter ( ) ,
941- ]
942- . into_iter ( )
943- . flatten ( )
944- . next ( )
945- // Limit to registry as that is the only source with meaningful alternative versions
946- . filter ( |s| s. source_id ( ) . is_registry ( ) ) ?;
947- let query = crate :: core:: dependency:: Dependency :: parse (
948- package_id. name ( ) ,
949- None ,
950- package_id. source_id ( ) ,
951- )
952- . expect ( "already a valid dependency" ) ;
953- Some ( query)
954- }
9551037}
0 commit comments