/// The user gives a list of patterns to say they only want to see the log
/// entries that affected those files. The patterns are given relative to the directory
/// they're calling from, not relative to the repository root, but we need to then
/// strip the repository path off the canonicalized filter path so
/// that we can match the file paths as they appear in the pijul change files, which
/// are recorded relative to the repository's root.
fn mk_filters(repo_path: &Path, mut pats: Vec<String>) -> Result<Vec<String>, anyhow::Error> {
for pat in &mut pats {
let canon_path = match Path::new(pat).canonicalize() {
Err(e) if matches!(e.kind(), std::io::ErrorKind::NotFound) => {
return Err(anyhow::Error::msg(format!(
"pijul log couldn't find a file or directory corresponding to `{}`",
pat
)))
}
Err(e) => return Err(e.into()),
Ok(p) => p,
};
match canon_path.strip_prefix(repo_path) {
Err(_) => {
return Err(anyhow::Error::msg(format!(
"pijul log couldn't assemble file prefix for pattern `{}`",
pat
)))
}
Ok(stripped) => {
*pat = format!("{}", stripped.display());
}
};
}
debug!("log filters: {:#?}\n", pats);
Ok(pats)
}
/// Figure out whether a given change touched one of the files
/// the user is filtering for.
fn filter_hit(
filters: &[String],
h: libpijul::Hash,
changes: &filesystem::FileSystem,
) -> Result<bool, anyhow::Error> {
match changes.filename(&h).to_str() {
None => {
return Err(anyhow::Error::msg(format!(
"internal error: changefile had non-utf8 name"
)))
}
Some(changefile_name) => {
let changefile = libpijul::change::ChangeFile::open(h, changefile_name)?;
if changefile.hashed().changes.iter().any(|hunk| {
filters
.iter()
.any(|f| hunk.path().starts_with(f) || hunk.path() == f)
}) {
return Ok(true);
}
Ok(false)
}
}
}