use anyhow::Result;
use crate::index::git::{DiffFile, DiffStatus};
use crate::storage::VectorStore;
use crate::types::Chunk;
use super::context::{SeedChunk, SeedSource};
pub async fn map_diff_to_chunks(
diff_files: &[DiffFile],
vector_store: &VectorStore,
repo: Option<&str>,
) -> Result<Vec<SeedChunk>> {
let mut seeds: Vec<SeedChunk> = Vec::new();
for diff_file in diff_files {
if diff_file.status == DiffStatus::Deleted {
continue;
}
let chunks = vector_store
.get_chunks_for_file(&diff_file.path, repo)
.await?;
for chunk in chunks {
let score = overlap_score(&chunk, diff_file);
if score > 0.0 {
seeds.push(SeedChunk {
chunk,
score,
source: SeedSource::Diff {
status: diff_file.status.to_string(),
added_lines: diff_file.added_lines.len(),
removed_lines: diff_file.removed_lines.len(),
},
repo: None, });
}
}
}
seeds.sort_by(|a, b| {
b.score
.partial_cmp(&a.score)
.unwrap_or(std::cmp::Ordering::Equal)
});
Ok(seeds)
}
fn overlap_score(chunk: &Chunk, diff_file: &DiffFile) -> f32 {
let chunk_start = chunk.start_line;
let chunk_end = chunk.end_line;
let chunk_lines = (chunk_end - chunk_start + 1) as f32;
if chunk_lines <= 0.0 {
return 0.0;
}
let overlapping = diff_file
.added_lines
.iter()
.filter(|&&line| line >= chunk_start && line <= chunk_end)
.count() as f32;
overlapping / chunk_lines
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::ChunkType;
fn make_chunk(id: &str, file: &str, start: u32, end: u32) -> Chunk {
Chunk {
id: id.to_string(),
file_path: file.to_string(),
chunk_type: ChunkType::Function,
name: Some(format!("fn_{}", id)),
start_line: start,
end_line: end,
content: "fn test() {}".to_string(),
language: "rust".to_string(),
tags: String::new(),
}
}
fn make_diff(path: &str, added: Vec<u32>, removed: Vec<u32>, status: DiffStatus) -> DiffFile {
DiffFile {
path: path.to_string(),
added_lines: added,
removed_lines: removed,
status,
}
}
#[test]
fn test_overlap_score_full_overlap() {
let chunk = make_chunk("c1", "a.rs", 10, 14); let diff = make_diff(
"a.rs",
vec![10, 11, 12, 13, 14],
vec![],
DiffStatus::Modified,
);
let score = overlap_score(&chunk, &diff);
assert!((score - 1.0).abs() < 0.001);
}
#[test]
fn test_overlap_score_partial_overlap() {
let chunk = make_chunk("c1", "a.rs", 10, 19); let diff = make_diff("a.rs", vec![15, 16, 17], vec![], DiffStatus::Modified);
let score = overlap_score(&chunk, &diff);
assert!((score - 0.3).abs() < 0.001); }
#[test]
fn test_overlap_score_no_overlap() {
let chunk = make_chunk("c1", "a.rs", 10, 19); let diff = make_diff("a.rs", vec![1, 2, 3, 25, 26], vec![], DiffStatus::Modified);
let score = overlap_score(&chunk, &diff);
assert!((score - 0.0).abs() < 0.001);
}
#[test]
fn test_overlap_score_only_added_lines_count() {
let chunk = make_chunk("c1", "a.rs", 10, 14); let diff = make_diff(
"a.rs",
vec![12], vec![10, 11, 12], DiffStatus::Modified,
);
let score = overlap_score(&chunk, &diff);
assert!((score - 0.2).abs() < 0.001); }
#[test]
fn test_overlap_score_single_line_chunk() {
let chunk = make_chunk("c1", "a.rs", 5, 5); let diff = make_diff("a.rs", vec![5], vec![], DiffStatus::Modified);
let score = overlap_score(&chunk, &diff);
assert!((score - 1.0).abs() < 0.001);
}
}