weavatrix_scan/stateful_walk/
mod.rs1use crate::runtime::ParallelRuntime;
2use crate::walk_types::{DirectoryIdentity, FileSystemId, WalkEntry, WalkError, WalkOptions};
3use crate::walker::Walker;
4use std::collections::HashSet;
5use std::path::{Path, PathBuf};
6use std::sync::Arc;
7
8mod parallel;
9mod serial;
10
11pub use parallel::ParallelStatefulWalker;
12
13type DirectoryProcessor<R, E> = Arc<
14 dyn Fn(usize, &Path, &mut R, &mut Vec<Result<StatefulWalkEntry<E>, WalkError>>)
15 + Send
16 + Sync
17 + 'static,
18>;
19
20#[derive(Debug)]
22pub struct StatefulWalkEntry<E> {
23 entry: WalkEntry,
24 pub state: E,
26 read_children: bool,
27}
28
29impl<E> StatefulWalkEntry<E> {
30 #[must_use]
31 pub const fn entry(&self) -> &WalkEntry {
32 &self.entry
33 }
34
35 #[must_use]
36 pub fn path(&self) -> &Path {
37 self.entry.path()
38 }
39
40 #[must_use]
41 pub const fn depth(&self) -> usize {
42 self.entry.depth()
43 }
44
45 #[must_use]
46 pub const fn is_file(&self) -> bool {
47 self.entry.is_file()
48 }
49
50 #[must_use]
51 pub const fn is_dir(&self) -> bool {
52 self.entry.is_dir()
53 }
54
55 #[must_use]
56 pub const fn read_children(&self) -> bool {
57 self.read_children
58 }
59
60 pub const fn set_read_children(&mut self, enabled: bool) {
62 self.read_children = enabled && self.entry.is_dir() && self.entry.skip_reason().is_none();
63 }
64
65 #[must_use]
66 pub fn into_parts(self) -> (WalkEntry, E) {
67 (self.entry, self.state)
68 }
69}
70
71pub struct StatefulWalkBuilder<R, E> {
76 root: PathBuf,
77 options: WalkOptions,
78 root_read_dir_state: R,
79 processor: Option<DirectoryProcessor<R, E>>,
80 parallelism: usize,
81 runtime: ParallelRuntime,
82}
83
84impl<R, E> StatefulWalkBuilder<R, E>
85where
86 R: Clone + Send + 'static,
87 E: Default + Send + 'static,
88{
89 #[must_use]
90 pub fn new(root: impl Into<PathBuf>, root_read_dir_state: R) -> Self {
91 Self {
92 root: root.into(),
93 options: WalkOptions::default(),
94 root_read_dir_state,
95 processor: None,
96 parallelism: 0,
97 runtime: ParallelRuntime::global(),
98 }
99 }
100
101 #[must_use]
102 pub const fn options(mut self, options: WalkOptions) -> Self {
103 self.options = options;
104 self
105 }
106
107 #[must_use]
109 pub const fn with_parallelism(mut self, parallelism: usize) -> Self {
110 self.parallelism = parallelism;
111 self
112 }
113
114 #[must_use]
116 pub fn runtime(mut self, runtime: ParallelRuntime) -> Self {
117 self.runtime = runtime;
118 self
119 }
120
121 #[must_use]
127 pub fn process_read_dir<F>(mut self, processor: F) -> Self
128 where
129 F: Fn(usize, &Path, &mut R, &mut Vec<Result<StatefulWalkEntry<E>, WalkError>>)
130 + Send
131 + Sync
132 + 'static,
133 {
134 self.processor = Some(Arc::new(processor));
135 self
136 }
137
138 pub fn build(self) -> Result<StatefulWalker<R, E>, WalkError> {
144 StatefulWalker::new(self)
145 }
146
147 pub fn build_parallel_ordered(
158 self,
159 capacity: usize,
160 ) -> Result<ParallelStatefulWalker<E>, WalkError> {
161 ParallelStatefulWalker::start(self, capacity)
162 }
163}
164
165struct DirectoryTask<R> {
166 path: PathBuf,
167 depth: usize,
168 identity: Option<DirectoryIdentity>,
169 ancestors: HashSet<DirectoryIdentity>,
170 read_state: R,
171}
172
173struct DirectoryFrame<R, E> {
174 entries: std::vec::IntoIter<Result<StatefulWalkEntry<E>, WalkError>>,
175 child_state: R,
176 ancestors: HashSet<DirectoryIdentity>,
177}
178
179pub struct StatefulWalker<R, E> {
181 root: Arc<PathBuf>,
182 root_file_system: Option<FileSystemId>,
183 options: WalkOptions,
184 processor: Option<DirectoryProcessor<R, E>>,
185 root_entry: Option<StatefulWalkEntry<E>>,
186 pending: Option<DirectoryTask<R>>,
187 frames: Vec<DirectoryFrame<R, E>>,
188}