weavatrix_scan/selection/
mod.rs1use crate::config::ScanOptions;
2use crate::error::{Error, Result};
3use crate::ignore::{RepositoryMatch, RepositoryMatcher};
4use crate::report::{IgnoreSourceEvidence, ScanWarning, SkipKind};
5use crate::scan_match::skip_kind_for_match;
6use crate::walk_platform::directory_info;
7use crate::walk_types::{FileSystemId, WalkEntry, WalkSkipReason};
8use std::fs;
9use std::path::{Component, Path, PathBuf};
10
11mod classification;
12mod construction;
13mod queries;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17#[non_exhaustive]
18pub enum SelectionDisposition {
19 SelectedFile,
21 TraverseDirectory,
23 Skipped(SkipKind),
25 Unselected,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub struct SelectionDecision {
33 disposition: SelectionDisposition,
34 repository_match: RepositoryMatch,
35}
36
37impl SelectionDecision {
38 #[must_use]
40 pub const fn disposition(self) -> SelectionDisposition {
41 self.disposition
42 }
43
44 #[must_use]
46 pub const fn repository_match(self) -> RepositoryMatch {
47 self.repository_match
48 }
49
50 #[must_use]
52 pub const fn is_selected(self) -> bool {
53 matches!(self.disposition, SelectionDisposition::SelectedFile)
54 }
55
56 #[must_use]
58 pub const fn should_descend(self) -> bool {
59 matches!(self.disposition, SelectionDisposition::TraverseDirectory)
60 }
61
62 #[must_use]
64 pub const fn skip_kind(self) -> Option<SkipKind> {
65 match self.disposition {
66 SelectionDisposition::Skipped(kind) => Some(kind),
67 SelectionDisposition::SelectedFile
68 | SelectionDisposition::TraverseDirectory
69 | SelectionDisposition::Unselected => None,
70 }
71 }
72
73 const fn selected(repository_match: RepositoryMatch) -> Self {
74 Self {
75 disposition: SelectionDisposition::SelectedFile,
76 repository_match,
77 }
78 }
79
80 const fn directory(repository_match: RepositoryMatch) -> Self {
81 Self {
82 disposition: SelectionDisposition::TraverseDirectory,
83 repository_match,
84 }
85 }
86
87 const fn skipped(kind: SkipKind, repository_match: RepositoryMatch) -> Self {
88 Self {
89 disposition: SelectionDisposition::Skipped(kind),
90 repository_match,
91 }
92 }
93
94 const fn unselected() -> Self {
95 Self {
96 disposition: SelectionDisposition::Unselected,
97 repository_match: RepositoryMatch::None,
98 }
99 }
100}
101
102#[derive(Debug, Clone)]
113pub struct SelectionMatcher {
114 repository: RepositoryMatcher,
115 options: ScanOptions,
116 root_file_system: Option<FileSystemId>,
117}
118
119fn relative_depth(path: &Path) -> usize {
120 path.components()
121 .filter(|component| matches!(component, Component::Normal(_)))
122 .count()
123}
124
125const fn skip_kind(reason: WalkSkipReason) -> SkipKind {
126 match reason {
127 WalkSkipReason::MaxDepth => SkipKind::MaxDepth,
128 WalkSkipReason::FileSystemBoundary => SkipKind::FileSystemBoundary,
129 WalkSkipReason::PathEscape => SkipKind::PathEscape,
130 WalkSkipReason::SymlinkLoop => SkipKind::SymlinkLoop,
131 }
132}