Skip to content

Commit 51c05df

Browse files
Use an iterator for logging upgrade events (#11301)
## Summary No behavior changes... This just separates the formatting from the collection of the results, and also fixes a bug whereby we didn't say "No changes detected" in some cases.
1 parent 8335a6d commit 51c05df

File tree

2 files changed

+115
-75
lines changed

2 files changed

+115
-75
lines changed

crates/uv/src/commands/project/lock.rs

Lines changed: 114 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,14 @@ pub(crate) async fn lock(
175175
{
176176
Ok(lock) => {
177177
if dry_run {
178-
let changed = if let LockResult::Changed(previous, lock) = &lock {
179-
report_upgrades(previous.as_ref(), lock, printer, dry_run)?
180-
} else {
181-
false
182-
};
178+
// In `--dry-run` mode, show all changes.
179+
let mut changed = false;
180+
if let LockResult::Changed(previous, lock) = &lock {
181+
for event in LockEvent::detect_changes(previous.as_ref(), lock, dry_run) {
182+
changed = true;
183+
writeln!(printer.stderr(), "{event}")?;
184+
}
185+
}
183186
if !changed {
184187
writeln!(
185188
printer.stderr(),
@@ -189,7 +192,9 @@ pub(crate) async fn lock(
189192
}
190193
} else {
191194
if let LockResult::Changed(Some(previous), lock) = &lock {
192-
report_upgrades(Some(previous), lock, printer, dry_run)?;
195+
for event in LockEvent::detect_changes(Some(previous), lock, dry_run) {
196+
writeln!(printer.stderr(), "{event}")?;
197+
}
193198
}
194199
}
195200

@@ -1072,104 +1077,138 @@ impl ValidatedLock {
10721077
}
10731078
}
10741079

1075-
/// Reports on the versions that were upgraded in the new lockfile.
1076-
///
1077-
/// Returns `true` if any upgrades were reported.
1078-
fn report_upgrades(
1079-
existing_lock: Option<&Lock>,
1080-
new_lock: &Lock,
1081-
printer: Printer,
1082-
dry_run: bool,
1083-
) -> anyhow::Result<bool> {
1084-
let existing_packages: FxHashMap<&PackageName, BTreeSet<Option<&Version>>> =
1085-
if let Some(existing_lock) = existing_lock {
1086-
existing_lock.packages().iter().fold(
1087-
FxHashMap::with_capacity_and_hasher(existing_lock.packages().len(), FxBuildHasher),
1080+
/// A modification to a lockfile.
1081+
#[derive(Debug, Clone)]
1082+
pub(crate) enum LockEvent<'lock> {
1083+
Update(
1084+
bool,
1085+
PackageName,
1086+
BTreeSet<Option<&'lock Version>>,
1087+
BTreeSet<Option<&'lock Version>>,
1088+
),
1089+
Add(bool, PackageName, BTreeSet<Option<&'lock Version>>),
1090+
Remove(bool, PackageName, BTreeSet<Option<&'lock Version>>),
1091+
}
1092+
1093+
impl<'lock> LockEvent<'lock> {
1094+
/// Detect the change events between an (optional) existing and updated lockfile.
1095+
pub(crate) fn detect_changes(
1096+
existing_lock: Option<&'lock Lock>,
1097+
new_lock: &'lock Lock,
1098+
dry_run: bool,
1099+
) -> impl Iterator<Item = Self> {
1100+
// Identify the package-versions in the existing lockfile.
1101+
let mut existing_packages: FxHashMap<&PackageName, BTreeSet<Option<&Version>>> =
1102+
if let Some(existing_lock) = existing_lock {
1103+
existing_lock.packages().iter().fold(
1104+
FxHashMap::with_capacity_and_hasher(
1105+
existing_lock.packages().len(),
1106+
FxBuildHasher,
1107+
),
1108+
|mut acc, package| {
1109+
acc.entry(package.name())
1110+
.or_default()
1111+
.insert(package.version());
1112+
acc
1113+
},
1114+
)
1115+
} else {
1116+
FxHashMap::default()
1117+
};
1118+
1119+
// Identify the package-versions in the updated lockfile.
1120+
let mut new_packages: FxHashMap<&PackageName, BTreeSet<Option<&Version>>> =
1121+
new_lock.packages().iter().fold(
1122+
FxHashMap::with_capacity_and_hasher(new_lock.packages().len(), FxBuildHasher),
10881123
|mut acc, package| {
10891124
acc.entry(package.name())
10901125
.or_default()
10911126
.insert(package.version());
10921127
acc
10931128
},
1094-
)
1095-
} else {
1096-
FxHashMap::default()
1097-
};
1129+
);
10981130

1099-
let new_distributions: FxHashMap<&PackageName, BTreeSet<Option<&Version>>> =
1100-
new_lock.packages().iter().fold(
1101-
FxHashMap::with_capacity_and_hasher(new_lock.packages().len(), FxBuildHasher),
1102-
|mut acc, package| {
1103-
acc.entry(package.name())
1104-
.or_default()
1105-
.insert(package.version());
1106-
acc
1107-
},
1108-
);
1131+
let names = existing_packages
1132+
.keys()
1133+
.chain(new_packages.keys())
1134+
.map(|name| (*name).clone())
1135+
.collect::<BTreeSet<_>>();
1136+
1137+
names.into_iter().filter_map(move |name| {
1138+
match (existing_packages.remove(&name), new_packages.remove(&name)) {
1139+
(Some(existing_versions), Some(new_versions)) => {
1140+
if existing_versions != new_versions {
1141+
Some(Self::Update(dry_run, name, existing_versions, new_versions))
1142+
} else {
1143+
None
1144+
}
1145+
}
1146+
(Some(existing_versions), None) => {
1147+
Some(Self::Remove(dry_run, name, existing_versions))
1148+
}
1149+
(None, Some(new_versions)) => Some(Self::Add(dry_run, name, new_versions)),
1150+
(None, None) => {
1151+
unreachable!("The key `{name}` should exist in at least one of the maps");
1152+
}
1153+
}
1154+
})
1155+
}
1156+
}
11091157

1110-
let mut updated = false;
1111-
for name in existing_packages
1112-
.keys()
1113-
.chain(new_distributions.keys())
1114-
.collect::<BTreeSet<_>>()
1115-
{
1158+
impl std::fmt::Display for LockEvent<'_> {
1159+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
11161160
/// Format a version for inclusion in the upgrade report.
11171161
fn format_version(version: Option<&Version>) -> String {
11181162
version
11191163
.map(|version| format!("v{version}"))
11201164
.unwrap_or_else(|| "(dynamic)".to_string())
11211165
}
11221166

1123-
updated = true;
1124-
match (existing_packages.get(name), new_distributions.get(name)) {
1125-
(Some(existing_versions), Some(new_versions)) => {
1126-
if existing_versions != new_versions {
1127-
let existing_versions = existing_versions
1128-
.iter()
1129-
.map(|version| format_version(*version))
1130-
.collect::<Vec<_>>()
1131-
.join(", ");
1132-
let new_versions = new_versions
1133-
.iter()
1134-
.map(|version| format_version(*version))
1135-
.collect::<Vec<_>>()
1136-
.join(", ");
1137-
writeln!(
1138-
printer.stderr(),
1139-
"{} {name} {existing_versions} -> {new_versions}",
1140-
if dry_run { "Update" } else { "Updated" }.green().bold()
1141-
)?;
1142-
}
1143-
}
1144-
(Some(existing_versions), None) => {
1167+
match self {
1168+
Self::Update(dry_run, name, existing_versions, new_versions) => {
11451169
let existing_versions = existing_versions
11461170
.iter()
11471171
.map(|version| format_version(*version))
11481172
.collect::<Vec<_>>()
11491173
.join(", ");
1150-
writeln!(
1151-
printer.stderr(),
1152-
"{} {name} {existing_versions}",
1153-
if dry_run { "Remove" } else { "Removed" }.red().bold()
1154-
)?;
1174+
let new_versions = new_versions
1175+
.iter()
1176+
.map(|version| format_version(*version))
1177+
.collect::<Vec<_>>()
1178+
.join(", ");
1179+
1180+
write!(
1181+
f,
1182+
"{} {name} {existing_versions} -> {new_versions}",
1183+
if *dry_run { "Update" } else { "Updated" }.green().bold()
1184+
)
11551185
}
1156-
(None, Some(new_versions)) => {
1186+
Self::Add(dry_run, name, new_versions) => {
11571187
let new_versions = new_versions
11581188
.iter()
11591189
.map(|version| format_version(*version))
11601190
.collect::<Vec<_>>()
11611191
.join(", ");
1162-
writeln!(
1163-
printer.stderr(),
1192+
1193+
write!(
1194+
f,
11641195
"{} {name} {new_versions}",
1165-
if dry_run { "Add" } else { "Added" }.green().bold()
1166-
)?;
1196+
if *dry_run { "Add" } else { "Added" }.green().bold()
1197+
)
11671198
}
1168-
(None, None) => {
1169-
unreachable!("The key `{name}` should exist in at least one of the maps");
1199+
Self::Remove(dry_run, name, existing_versions) => {
1200+
let existing_versions = existing_versions
1201+
.iter()
1202+
.map(|version| format_version(*version))
1203+
.collect::<Vec<_>>()
1204+
.join(", ");
1205+
1206+
write!(
1207+
f,
1208+
"{} {name} {existing_versions}",
1209+
if *dry_run { "Remove" } else { "Removed" }.red().bold()
1210+
)
11701211
}
11711212
}
11721213
}
1173-
1174-
Ok(updated)
11751214
}

crates/uv/tests/it/lock.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18736,6 +18736,7 @@ fn lock_dry_run_noop() -> Result<()> {
1873618736

1873718737
----- stderr -----
1873818738
Resolved 5 packages in [TIME]
18739+
No lockfile changes detected
1873918740
"###);
1874018741

1874118742
Ok(())

0 commit comments

Comments
 (0)