#[derive(Clone, Debug)]
pub(crate) struct DiffHunk {
pub text: String,
pub modified_lines: Vec<usize>,
}
pub(crate) fn parse_hunks(diff: &str) -> Vec<DiffHunk> {
let mut hunks: Vec<DiffHunk> = Vec::new();
let mut current_text = String::new();
let mut current_modified: Vec<usize> = Vec::new();
let mut current_orig_line: usize = 0;
let mut in_hunk = false;
for line in diff.lines() {
if line.starts_with("@@ -") {
if in_hunk {
hunks.push(DiffHunk {
text: std::mem::take(&mut current_text),
modified_lines: std::mem::take(&mut current_modified),
});
}
current_text = format!("{}\n", line);
current_modified = Vec::new();
current_orig_line = parse_hunk_start(line).unwrap_or(1);
in_hunk = true;
} else if !in_hunk {
continue;
} else if line.starts_with('-') {
current_text.push_str(line);
current_text.push('\n');
current_modified.push(current_orig_line);
current_orig_line += 1;
} else if line.starts_with('+') {
current_text.push_str(line);
current_text.push('\n');
} else if line.starts_with('\\') {
current_text.push_str(line);
current_text.push('\n');
} else {
current_text.push_str(line);
current_text.push('\n');
current_orig_line += 1;
}
}
if in_hunk {
hunks.push(DiffHunk {
text: current_text,
modified_lines: current_modified,
});
}
hunks
}
pub(crate) fn parse_hunk_start(line: &str) -> Option<usize> {
let line = line.strip_prefix("@@ -")?;
let end = line.find([',', ' '])?;
line[..end].parse().ok()
}
pub(crate) fn build_hunk_patch(path: &str, hunks: &[impl std::borrow::Borrow<DiffHunk>]) -> String {
let mut patch = String::new();
patch.push_str(&format!("--- a/{}\n", path));
patch.push_str(&format!("+++ b/{}\n", path));
for hunk in hunks {
patch.push_str(&hunk.borrow().text);
}
patch
}
#[cfg(test)]
#[path = "diff_test.rs"]
mod tests;