use crate::config::ScanOptions;
use crate::error::{Error, Result};
use crate::ignore::{RepositoryMatch, RepositoryMatcher};
use crate::report::{IgnoreSourceEvidence, ScanWarning, SkipKind};
use crate::scan_match::skip_kind_for_match;
use crate::walk_platform::directory_info;
use crate::walk_types::{FileSystemId, WalkEntry, WalkSkipReason};
use std::fs;
use std::path::{Component, Path, PathBuf};
mod classification;
mod construction;
mod queries;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum SelectionDisposition {
SelectedFile,
TraverseDirectory,
Skipped(SkipKind),
Unselected,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SelectionDecision {
disposition: SelectionDisposition,
repository_match: RepositoryMatch,
}
impl SelectionDecision {
#[must_use]
pub const fn disposition(self) -> SelectionDisposition {
self.disposition
}
#[must_use]
pub const fn repository_match(self) -> RepositoryMatch {
self.repository_match
}
#[must_use]
pub const fn is_selected(self) -> bool {
matches!(self.disposition, SelectionDisposition::SelectedFile)
}
#[must_use]
pub const fn should_descend(self) -> bool {
matches!(self.disposition, SelectionDisposition::TraverseDirectory)
}
#[must_use]
pub const fn skip_kind(self) -> Option<SkipKind> {
match self.disposition {
SelectionDisposition::Skipped(kind) => Some(kind),
SelectionDisposition::SelectedFile
| SelectionDisposition::TraverseDirectory
| SelectionDisposition::Unselected => None,
}
}
const fn selected(repository_match: RepositoryMatch) -> Self {
Self {
disposition: SelectionDisposition::SelectedFile,
repository_match,
}
}
const fn directory(repository_match: RepositoryMatch) -> Self {
Self {
disposition: SelectionDisposition::TraverseDirectory,
repository_match,
}
}
const fn skipped(kind: SkipKind, repository_match: RepositoryMatch) -> Self {
Self {
disposition: SelectionDisposition::Skipped(kind),
repository_match,
}
}
const fn unselected() -> Self {
Self {
disposition: SelectionDisposition::Unselected,
repository_match: RepositoryMatch::None,
}
}
}
#[derive(Debug, Clone)]
pub struct SelectionMatcher {
repository: RepositoryMatcher,
options: ScanOptions,
root_file_system: Option<FileSystemId>,
}
fn relative_depth(path: &Path) -> usize {
path.components()
.filter(|component| matches!(component, Component::Normal(_)))
.count()
}
const fn skip_kind(reason: WalkSkipReason) -> SkipKind {
match reason {
WalkSkipReason::MaxDepth => SkipKind::MaxDepth,
WalkSkipReason::FileSystemBoundary => SkipKind::FileSystemBoundary,
WalkSkipReason::PathEscape => SkipKind::PathEscape,
WalkSkipReason::SymlinkLoop => SkipKind::SymlinkLoop,
}
}