chore: cargo fmt after Rust 1.93 toolchain bump
This commit is contained in:
@@ -303,6 +303,67 @@ pub fn update_source_map(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Regenerate the source map from scratch for all tracked source files in `worktree`.
|
||||
///
|
||||
/// Uses `git ls-files` to enumerate every tracked Rust and TypeScript file, extracts
|
||||
/// their public item signatures, and writes a fresh JSON map sorted by key. Running
|
||||
/// twice with unchanged source produces byte-identical output (deterministic).
|
||||
///
|
||||
/// Unlike [`update_for_worktree`], this path cannot leave stale entries: every file in
|
||||
/// the map was present and tracked at the time of writing.
|
||||
pub fn regenerate_source_map(worktree: &Path, source_map_path: &Path) -> Result<(), String> {
|
||||
let output = Command::new("git")
|
||||
.args(["ls-files"])
|
||||
.current_dir(worktree)
|
||||
.output()
|
||||
.map_err(|e| format!("git ls-files: {e}"))?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(format!(
|
||||
"git ls-files failed: {}",
|
||||
String::from_utf8_lossy(&output.stderr).trim()
|
||||
));
|
||||
}
|
||||
|
||||
// Use BTreeMap so keys are sorted alphabetically → deterministic output.
|
||||
let mut entries: std::collections::BTreeMap<String, Vec<serde_json::Value>> =
|
||||
std::collections::BTreeMap::new();
|
||||
|
||||
for rel_path in String::from_utf8_lossy(&output.stdout).lines() {
|
||||
if rel_path.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let abs_path = worktree.join(rel_path);
|
||||
if !abs_path.exists() {
|
||||
continue;
|
||||
}
|
||||
let ext = abs_path.extension().and_then(|e| e.to_str()).unwrap_or("");
|
||||
let items: Vec<serde_json::Value> = match ext {
|
||||
"rs" => RustAdapter::extract_items(&abs_path)
|
||||
.into_iter()
|
||||
.map(serde_json::Value::String)
|
||||
.collect(),
|
||||
"ts" | "tsx" => TypeScriptAdapter::extract_items(&abs_path)
|
||||
.into_iter()
|
||||
.map(serde_json::Value::String)
|
||||
.collect(),
|
||||
_ => continue,
|
||||
};
|
||||
entries.insert(rel_path.to_string(), items);
|
||||
}
|
||||
|
||||
let map: serde_json::Map<String, serde_json::Value> = entries
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, serde_json::Value::Array(v)))
|
||||
.collect();
|
||||
|
||||
if let Some(parent) = source_map_path.parent() {
|
||||
std::fs::create_dir_all(parent).map_err(|e| format!("create_dir_all: {e}"))?;
|
||||
}
|
||||
|
||||
write_map(source_map_path, map)
|
||||
}
|
||||
|
||||
/// Update the source map for files that changed since `base_branch` in `worktree_path`.
|
||||
///
|
||||
/// 1. Runs `git diff --name-only {base_branch}...HEAD` in the worktree.
|
||||
@@ -311,7 +372,12 @@ pub fn update_source_map(
|
||||
///
|
||||
/// Errors are returned as `Err(String)`; callers in the spawn flow treat them as
|
||||
/// non-blocking warnings.
|
||||
pub fn update_for_worktree(
|
||||
///
|
||||
/// # Note
|
||||
/// This incremental path is retained for testing only. Production map writes use
|
||||
/// [`regenerate_source_map`] which cannot leave stale entries.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn update_for_worktree(
|
||||
worktree_path: &Path,
|
||||
base_branch: &str,
|
||||
source_map_path: &Path,
|
||||
@@ -894,6 +960,59 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// AC4: running `regenerate_source_map` twice on the same source tree produces
|
||||
/// byte-identical output.
|
||||
#[test]
|
||||
fn regenerate_source_map_is_deterministic() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
init_git_repo(tmp.path());
|
||||
|
||||
// Add a few tracked files and commit them.
|
||||
write_rs(
|
||||
tmp.path(),
|
||||
"alpha.rs",
|
||||
"//! Alpha module.\n\n/// Does alpha.\npub fn alpha() {}\n",
|
||||
);
|
||||
write_rs(
|
||||
tmp.path(),
|
||||
"beta.rs",
|
||||
"//! Beta module.\n\n/// Does beta.\npub fn beta() {}\n",
|
||||
);
|
||||
Command::new("git")
|
||||
.args(["add", "alpha.rs", "beta.rs"])
|
||||
.current_dir(tmp.path())
|
||||
.output()
|
||||
.unwrap();
|
||||
Command::new("git")
|
||||
.args(["commit", "-m", "add files"])
|
||||
.current_dir(tmp.path())
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
let map_path = tmp.path().join("source-map.json");
|
||||
|
||||
let result1 = regenerate_source_map(tmp.path(), &map_path);
|
||||
assert!(
|
||||
result1.is_ok(),
|
||||
"first regenerate failed: {:?}",
|
||||
result1.err()
|
||||
);
|
||||
let first = std::fs::read_to_string(&map_path).unwrap();
|
||||
|
||||
let result2 = regenerate_source_map(tmp.path(), &map_path);
|
||||
assert!(
|
||||
result2.is_ok(),
|
||||
"second regenerate failed: {:?}",
|
||||
result2.err()
|
||||
);
|
||||
let second = std::fs::read_to_string(&map_path).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
first, second,
|
||||
"regenerate_source_map must be byte-identical on repeated runs"
|
||||
);
|
||||
}
|
||||
|
||||
/// `relative_key` strips the root prefix from an absolute path.
|
||||
#[test]
|
||||
fn relative_key_strips_root_prefix() {
|
||||
|
||||
Reference in New Issue
Block a user