Skip to content

Commit 262af91

Browse files
branchseerclaude
andcommitted
refactor: use BTreeMap for folder fingerprint entries
Enables sorted lockstep diffing consistent with detect_globbed_input_change. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b393197 commit 262af91

1 file changed

Lines changed: 32 additions & 20 deletions

File tree

crates/vite_task/src/session/execute/fingerprint.rs

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//! fingerprints of file system state after task execution.
55
66
use std::{
7+
collections::BTreeMap,
78
fs::File,
89
hash::Hasher as _,
910
io::{self, BufRead, Read},
@@ -38,8 +39,8 @@ pub enum PathFingerprint {
3839
/// Directory with optional entry listing.
3940
/// `Folder(None)` means the directory was opened but entries were not read
4041
/// (e.g., for `openat` calls).
41-
/// `Folder(Some(_))` contains the directory entries.
42-
Folder(Option<HashMap<Str, DirEntryKind>>),
42+
/// `Folder(Some(_))` contains the directory entries sorted by name.
43+
Folder(Option<BTreeMap<Str, DirEntryKind>>),
4344
}
4445

4546
/// Kind of directory entry
@@ -141,27 +142,38 @@ fn determine_change_kind<'a>(
141142
}
142143

143144
/// Determine whether a folder change is an addition or removal by comparing entries.
145+
/// Both maps are `BTreeMap` so we iterate them in sorted lockstep.
144146
/// Returns the specific entry name that was added or removed, if identifiable.
145147
fn determine_folder_change_kind<'a>(
146-
old: Option<&'a HashMap<Str, DirEntryKind>>,
147-
new: Option<&'a HashMap<Str, DirEntryKind>>,
148+
old: Option<&'a BTreeMap<Str, DirEntryKind>>,
149+
new: Option<&'a BTreeMap<Str, DirEntryKind>>,
148150
) -> (InputChangeKind, Option<&'a Str>) {
149-
match (old, new) {
150-
(Some(old_entries), Some(new_entries)) => {
151-
for key in old_entries.keys() {
152-
if !new_entries.contains_key(key) {
153-
return (InputChangeKind::Removed, Some(key));
154-
}
155-
}
156-
for key in new_entries.keys() {
157-
if !old_entries.contains_key(key) {
158-
return (InputChangeKind::Added, Some(key));
151+
let (Some(old_entries), Some(new_entries)) = (old, new) else {
152+
return (InputChangeKind::Added, None);
153+
};
154+
155+
let mut old_iter = old_entries.iter();
156+
let mut new_iter = new_entries.iter();
157+
let mut o = old_iter.next();
158+
let mut n = new_iter.next();
159+
160+
loop {
161+
match (o, n) {
162+
(None, None) => return (InputChangeKind::Added, None),
163+
(Some((name, _)), None) => return (InputChangeKind::Removed, Some(name)),
164+
(None, Some((name, _))) => return (InputChangeKind::Added, Some(name)),
165+
(Some((ok, ov)), Some((nk, nv))) => match ok.cmp(nk) {
166+
std::cmp::Ordering::Equal => {
167+
if ov != nv {
168+
return (InputChangeKind::Added, Some(ok));
169+
}
170+
o = old_iter.next();
171+
n = new_iter.next();
159172
}
160-
}
161-
// Same keys but different entry kinds — default to Added
162-
(InputChangeKind::Added, None)
173+
std::cmp::Ordering::Less => return (InputChangeKind::Removed, Some(ok)),
174+
std::cmp::Ordering::Greater => return (InputChangeKind::Added, Some(nk)),
175+
},
163176
}
164-
_ => (InputChangeKind::Added, None),
165177
}
166178
}
167179

@@ -248,7 +260,7 @@ fn process_directory(
248260
return Ok(PathFingerprint::Folder(None));
249261
}
250262

251-
let mut entries = HashMap::new();
263+
let mut entries = BTreeMap::new();
252264
for entry in std::fs::read_dir(path)? {
253265
let entry = entry?;
254266
let name = entry.file_name();
@@ -286,7 +298,7 @@ fn process_directory_unix(file: &File, path_read: PathRead) -> anyhow::Result<Pa
286298
let fd = file.as_fd();
287299
let mut dir = nix::dir::Dir::from_fd(fd.try_clone_to_owned()?)?;
288300

289-
let mut entries = HashMap::new();
301+
let mut entries = BTreeMap::new();
290302
for entry in dir.iter() {
291303
let entry = entry?;
292304
let name = entry.file_name().to_bytes();

0 commit comments

Comments
 (0)