use crate::interner::StringInterner;
use crate::types::{ChangeType, ChangedFile};
pub struct DiffParser<'a> {
interner: &'a StringInterner,
}
impl<'a> DiffParser<'a> {
pub fn new(interner: &'a StringInterner) -> Self {
Self { interner }
}
pub fn parse_diff_line(&self, line: &[u8]) -> Option<ChangedFile> {
use memchr::memchr;
let tab_pos = memchr(b'\t', line)?;
let change_type = ChangeType::from_byte(line[0])?;
match change_type {
ChangeType::Renamed | ChangeType::Copied => {
let second_tab_offset = memchr(b'\t', &line[tab_pos + 1..])?;
let second_tab = tab_pos + 1 + second_tab_offset;
let old_path = std::str::from_utf8(&line[tab_pos + 1..second_tab]).ok()?;
let new_path = std::str::from_utf8(&line[second_tab + 1..]).ok()?;
Some(ChangedFile {
path: self.interner.intern(new_path.trim()),
change_type,
previous_path: Some(self.interner.intern(old_path.trim())),
is_symlink: false, submodule_depth: 0,
origin: crate::types::FileOrigin {
in_current_changes: true,
in_previous_failure: false,
in_previous_success: false,
},
})
}
_ => {
let path = std::str::from_utf8(&line[tab_pos + 1..]).ok()?;
Some(ChangedFile {
path: self.interner.intern(path.trim()),
change_type,
previous_path: None,
is_symlink: false,
submodule_depth: 0,
origin: crate::types::FileOrigin {
in_current_changes: true,
in_previous_failure: false,
in_previous_success: false,
},
})
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_modified() {
let interner = StringInterner::new();
let parser = DiffParser::new(&interner);
let line = b"M\tsrc/main.rs";
let file = parser.parse_diff_line(line).unwrap();
assert_eq!(file.change_type, ChangeType::Modified);
assert_eq!(interner.resolve(file.path), Some("src/main.rs"));
assert!(file.previous_path.is_none());
}
#[test]
fn test_parse_renamed() {
let interner = StringInterner::new();
let parser = DiffParser::new(&interner);
let line = b"R100\told/path.rs\tnew/path.rs";
let file = parser.parse_diff_line(line).unwrap();
assert_eq!(file.change_type, ChangeType::Renamed);
assert_eq!(interner.resolve(file.path), Some("new/path.rs"));
assert_eq!(
interner.resolve(file.previous_path.unwrap()),
Some("old/path.rs")
);
}
}