use std::path::Path;
use crate::{CodeLoreError, Options, Result};
pub struct PathsFilter {
globset: globset::GlobSet,
gitignore: ignore::gitignore::Gitignore,
}
impl PathsFilter {
pub fn from_opts(opts: &Options) -> Result<Self> {
let mut gb = ignore::gitignore::GitignoreBuilder::new(&opts.repo_path);
if !opts.include_ignored {
let gitignore_path = opts.repo_path.join(".gitignore");
if gitignore_path.is_file()
&& let Some(err) = gb.add(&gitignore_path)
{
return Err(CodeLoreError::Analysis(format!(
".gitignore at {}: {err}",
gitignore_path.display()
)));
}
let info_exclude = opts.repo_path.join(".git/info/exclude");
if info_exclude.is_file()
&& let Some(err) = gb.add(&info_exclude)
{
return Err(CodeLoreError::Analysis(format!(
".git/info/exclude at {}: {err}",
info_exclude.display()
)));
}
let codelore_ignore = opts.repo_path.join(".codeloreignore");
if codelore_ignore.is_file()
&& let Some(err) = gb.add(&codelore_ignore)
{
return Err(CodeLoreError::Analysis(format!(".codeloreignore: {err}")));
}
}
let gitignore = gb
.build()
.map_err(|e| CodeLoreError::Analysis(format!("build gitignore matcher: {e}")))?;
let mut gsb = globset::GlobSetBuilder::new();
for pat in &opts.exclude_patterns {
let g = globset::Glob::new(pat)
.map_err(|e| CodeLoreError::Analysis(format!("--exclude {pat:?}: {e}")))?;
gsb.add(g);
}
let globset = gsb
.build()
.map_err(|e| CodeLoreError::Analysis(format!("build --exclude globset: {e}")))?;
Ok(Self { globset, gitignore })
}
#[must_use]
pub fn is_excluded(&self, rel_path: &Path, is_dir: bool) -> bool {
if self.globset.is_match(rel_path) {
return true;
}
match self.gitignore.matched_path_or_any_parents(rel_path, is_dir) {
ignore::Match::Ignore(_) => true,
ignore::Match::Whitelist(_) | ignore::Match::None => false,
}
}
}
#[must_use]
pub fn is_git_metadata(rel_path: &Path) -> bool {
rel_path
.components()
.next()
.and_then(|c| c.as_os_str().to_str())
== Some(".git")
}