use git2::Repository;
use std::path::Path;
use crate::error::{Result, TuicrError};
use crate::model::{DiffLine, FileStatus, LineOrigin};
pub fn fetch_context_lines(
repo: &Repository,
file_path: &Path,
file_status: FileStatus,
start_line: u32,
end_line: u32,
) -> Result<Vec<DiffLine>> {
if start_line > end_line || start_line == 0 {
return Ok(Vec::new());
}
let content = match file_status {
FileStatus::Deleted => {
fetch_blob_content(repo, file_path)?
}
_ => {
let workdir = repo.workdir().ok_or(TuicrError::NotARepository)?;
let full_path = workdir.join(file_path);
std::fs::read_to_string(&full_path)?
}
};
let lines: Vec<&str> = content.lines().collect();
let mut result = Vec::new();
for line_num in start_line..=end_line {
let idx = (line_num - 1) as usize;
if idx < lines.len() {
result.push(DiffLine {
origin: LineOrigin::Context,
content: lines[idx].to_string(),
old_lineno: Some(line_num),
new_lineno: Some(line_num),
highlighted_spans: None,
});
}
}
Ok(result)
}
fn fetch_blob_content(repo: &Repository, file_path: &Path) -> Result<String> {
let head = repo.head()?.peel_to_tree()?;
let entry = head.get_path(file_path)?;
let blob = repo.find_blob(entry.id())?;
let content = std::str::from_utf8(blob.content())
.map_err(|e| TuicrError::CorruptedSession(format!("Invalid UTF-8 in file: {e}")))?;
Ok(content.to_string())
}
pub fn calculate_gap(
prev_hunk: Option<(&u32, &u32)>, current_new_start: u32,
) -> u32 {
match prev_hunk {
None => {
current_new_start.saturating_sub(1)
}
Some((prev_start, prev_count)) => {
let prev_end = prev_start + prev_count;
current_new_start.saturating_sub(prev_end)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_calculate_gap_before_first_hunk() {
let current_start = 10;
let gap = calculate_gap(None, current_start);
assert_eq!(gap, 9); }
#[test]
fn should_calculate_gap_between_hunks() {
let prev_start = 5;
let prev_count = 3; let current_start = 15;
let gap = calculate_gap(Some((&prev_start, &prev_count)), current_start);
assert_eq!(gap, 7); }
#[test]
fn should_return_zero_for_adjacent_hunks() {
let prev_start = 5;
let prev_count = 3; let current_start = 8;
let gap = calculate_gap(Some((&prev_start, &prev_count)), current_start);
assert_eq!(gap, 0);
}
#[test]
fn should_handle_overlapping_hunks() {
let prev_start = 5;
let prev_count = 10; let current_start = 12;
let gap = calculate_gap(Some((&prev_start, &prev_count)), current_start);
assert_eq!(gap, 0); }
}