#[derive(Debug)]
pub struct FileDiff {
pub filename : String,
pub diff : Vec<Hunk>,
}
#[derive(Debug)]
pub struct Hunk {
pub header: String, pub lines: Vec<DiffLine>,
}
#[derive(Debug, Clone)]
pub struct DiffLine {
pub content: String,
pub line_type: LineType,
}
#[derive(Debug, Clone, PartialEq)]
pub enum LineType {
Added,
Removed,
Unchanged,
Metadata,
}
pub fn parse_diff(raw_diff: &str) -> Vec<FileDiff> {
let mut file_diffs: Vec<FileDiff> = Vec::new();
let mut current_file: Option<FileDiff> = None;
let mut current_hunk: Option<Hunk> = None;
for line in raw_diff.lines() {
if line.starts_with("diff --git") {
if let Some(hunk) = current_hunk.take() {
if let Some(file) = &mut current_file {
file.diff.push(hunk);
}
}
if let Some(file) = current_file.take() {
file_diffs.push(file);
}
let seeded_name = line
.strip_prefix("diff --git ")
.and_then(|rest| {
let (a_part, b_part) = rest.split_once(" b/")?;
let b_path = b_part.trim();
if b_path == "/dev/null" || b_path.is_empty() {
a_part.strip_prefix("a/").map(|s| s.trim().to_string())
} else {
Some(b_path.to_string())
}
})
.unwrap_or_default();
current_file = Some(FileDiff {
filename: seeded_name,
diff: Vec::new(),
});
} else if line.starts_with("+++ b/") {
if let Some(file) = &mut current_file {
file.filename = line.trim_start_matches("+++ b/").to_string();
}
} else if line.starts_with("@@") {
if let Some(hunk) = current_hunk.take() {
if let Some(file) = &mut current_file {
file.diff.push(hunk);
}
}
current_hunk = Some(Hunk {
header: line.to_string(),
lines: Vec::new(),
});
} else if let Some(hunk) = &mut current_hunk {
if line.starts_with("\\ No newline") {
hunk.lines.push(DiffLine {
content: line.to_string(),
line_type: LineType::Metadata,
});
} else {
let line_type = if line.starts_with('+') {
LineType::Added
} else if line.starts_with('-') {
LineType::Removed
} else {
LineType::Unchanged
};
hunk.lines.push(DiffLine {
content: if line.is_empty() { String::new() } else { line[1..].to_string() },
line_type,
});
}
}
}
if let Some(hunk) = current_hunk.take() {
if let Some(file) = &mut current_file {
file.diff.push(hunk);
}
}
if let Some(file) = current_file.take() {
file_diffs.push(file);
}
file_diffs
}