Add filter to LogWalker

This is the first step towards adding a file history view. The filter
itself is not related to files specifically, though. It could also be
used for different purposes.
This commit is contained in:
Christoph Rüßler 2021-06-09 16:38:54 +02:00 committed by Stephan Dilly
parent 947c4a1a5e
commit f132722272

View File

@ -28,11 +28,16 @@ impl<'a> Ord for TimeOrderedCommit<'a> {
} }
} }
type LogWalkerFilter =
Box<dyn Fn(&Repository, &CommitId) -> Result<bool>>;
/// ///
pub struct LogWalker<'a> { pub struct LogWalker<'a> {
commits: BinaryHeap<TimeOrderedCommit<'a>>, commits: BinaryHeap<TimeOrderedCommit<'a>>,
visited: HashSet<Oid>, visited: HashSet<Oid>,
limit: usize, limit: usize,
repo: &'a Repository,
filter: Option<LogWalkerFilter>,
} }
impl<'a> LogWalker<'a> { impl<'a> LogWalker<'a> {
@ -47,9 +52,19 @@ impl<'a> LogWalker<'a> {
commits, commits,
limit, limit,
visited: HashSet::with_capacity(1000), visited: HashSet::with_capacity(1000),
repo,
filter: None,
}) })
} }
///
pub fn filter(self, filter: LogWalkerFilter) -> Self {
Self {
filter: Some(filter),
..self
}
}
/// ///
pub fn read(&mut self, out: &mut Vec<CommitId>) -> Result<usize> { pub fn read(&mut self, out: &mut Vec<CommitId>) -> Result<usize> {
let mut count = 0_usize; let mut count = 0_usize;
@ -59,7 +74,17 @@ impl<'a> LogWalker<'a> {
self.visit(p); self.visit(p);
} }
out.push(c.0.id().into()); let id: CommitId = c.0.id().into();
let commit_should_be_included =
if let Some(ref filter) = self.filter {
filter(self.repo, &id)?
} else {
true
};
if commit_should_be_included {
out.push(id);
}
count += 1; count += 1;
if count == self.limit { if count == self.limit {
@ -82,9 +107,10 @@ impl<'a> LogWalker<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::error::Result;
use crate::sync::{ use crate::sync::{
commit, get_commits_info, stage_add_file, commit, commit_files::get_commit_diff, get_commits_info,
tests::repo_init_empty, stage_add_file, tests::repo_init_empty,
}; };
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use std::{fs::File, io::Write, path::Path}; use std::{fs::File, io::Write, path::Path};
@ -144,4 +170,79 @@ mod tests {
Ok(()) Ok(())
} }
#[test]
fn test_logwalker_with_filter() -> Result<()> {
let file_path = Path::new("foo");
let second_file_path = Path::new("baz");
let (_td, repo) = repo_init_empty().unwrap();
let root = repo.path().parent().unwrap();
let repo_path = root.as_os_str().to_str().unwrap();
File::create(&root.join(file_path))?.write_all(b"a")?;
stage_add_file(repo_path, file_path).unwrap();
let _first_commit_id = commit(repo_path, "commit1").unwrap();
File::create(&root.join(second_file_path))?
.write_all(b"a")?;
stage_add_file(repo_path, second_file_path).unwrap();
let second_commit_id = commit(repo_path, "commit2").unwrap();
File::create(&root.join(file_path))?.write_all(b"b")?;
stage_add_file(repo_path, file_path).unwrap();
let _third_commit_id = commit(repo_path, "commit3").unwrap();
let diff_contains_baz = |repo: &Repository,
commit_id: &CommitId|
-> Result<bool> {
let diff = get_commit_diff(
&repo,
*commit_id,
Some("baz".into()),
)?;
let contains_file = diff.deltas().len() > 0;
Ok(contains_file)
};
let mut items = Vec::new();
let mut walker = LogWalker::new(&repo, 100)?
.filter(Box::new(diff_contains_baz));
walker.read(&mut items).unwrap();
assert_eq!(items.len(), 1);
assert_eq!(items[0], second_commit_id.into());
let mut items = Vec::new();
walker.read(&mut items).unwrap();
assert_eq!(items.len(), 0);
let diff_contains_bar = |repo: &Repository,
commit_id: &CommitId|
-> Result<bool> {
let diff = get_commit_diff(
&repo,
*commit_id,
Some("bar".into()),
)?;
let contains_file = diff.deltas().len() > 0;
Ok(contains_file)
};
let mut items = Vec::new();
let mut walker = LogWalker::new(&repo, 100)?
.filter(Box::new(diff_contains_bar));
walker.read(&mut items).unwrap();
assert_eq!(items.len(), 0);
Ok(())
}
} }