weavatrix-scan 0.5.3

Deterministic, safe repository scanner for code intelligence
Documentation
use crate::config::ScanOptions;
use crate::ignore::{IgnoreRules, RepositoryMatch, RepositoryMatcher};
use crate::path::normalized_relative_path;
use crate::scan_match::skip_kind_for_match;
use crate::walk_types::WalkEntry;

pub(super) enum PathAction {
    Continue,
    SkipDir,
    File(String),
}

pub(super) fn decide_path(
    entry: &WalkEntry,
    options: &ScanOptions,
    matcher: &RepositoryMatcher,
    prepared_rules: Option<&IgnoreRules>,
) -> PathAction {
    if entry.depth() == 0 {
        return PathAction::Continue;
    }
    if entry.skip_reason().is_some() {
        return if entry.is_dir() {
            PathAction::SkipDir
        } else {
            PathAction::Continue
        };
    }
    if entry.is_symlink() && !options.walk.follow_links {
        return PathAction::Continue;
    }
    if entry.depth() < options.effective_min_depth() && !entry.is_dir() {
        return PathAction::Continue;
    }
    let relative = normalized_relative_path(entry.relative_path());
    if entry.is_dir() {
        return decide_directory(entry, &relative, options, matcher, prepared_rules);
    }
    if entry.is_file() {
        decide_file(entry, relative, options, matcher, prepared_rules)
    } else {
        PathAction::Continue
    }
}

fn decide_directory(
    entry: &WalkEntry,
    relative: &str,
    options: &ScanOptions,
    matcher: &RepositoryMatcher,
    prepared_rules: Option<&IgnoreRules>,
) -> PathAction {
    let decision = match_entry(entry, relative, true, matcher, prepared_rules);
    if skip_kind_for_match(decision).is_some() {
        return PathAction::SkipDir;
    }
    if decision != RepositoryMatch::OverrideInclude
        && options.should_skip_directory(entry.file_name())
    {
        return PathAction::SkipDir;
    }
    PathAction::Continue
}

fn decide_file(
    entry: &WalkEntry,
    relative: String,
    options: &ScanOptions,
    matcher: &RepositoryMatcher,
    prepared_rules: Option<&IgnoreRules>,
) -> PathAction {
    let decision = match_entry(entry, &relative, false, matcher, prepared_rules);
    if skip_kind_for_match(decision).is_some() {
        return PathAction::Continue;
    }
    if decision != RepositoryMatch::OverrideInclude
        && !options.accepts_extension(entry.path(), &relative)
    {
        return PathAction::Continue;
    }
    PathAction::File(relative)
}

fn match_entry(
    entry: &WalkEntry,
    relative: &str,
    is_directory: bool,
    matcher: &RepositoryMatcher,
    prepared_rules: Option<&IgnoreRules>,
) -> RepositoryMatch {
    let parent = entry.path().parent().unwrap_or(entry.path());
    prepared_rules.map_or_else(
        || {
            matcher.matched_prepared(
                relative,
                parent,
                entry.path(),
                is_directory,
                entry.hidden(),
                true,
            )
        },
        |rules| {
            matcher.matched_with_rules(
                relative,
                entry.path(),
                is_directory,
                entry.hidden(),
                rules,
                true,
            )
        },
    )
}