Process every entry in `files`, calling `on_progress(n, total, path)` once per file. Returns one tuple per successfully-processed file; files whose worker crashed or that had no extractor / read error are silently skipped (logged to stderr).
(
self,
files: Vec<String>,
on_progress: F,
per_file_timeout: Duration,
)
| 215 | /// files whose worker crashed or that had no extractor / read error are |
| 216 | /// silently skipped (logged to stderr). |
| 217 | pub fn extract_files<F>( |
| 218 | self, |
| 219 | files: Vec<String>, |
| 220 | on_progress: F, |
| 221 | per_file_timeout: Duration, |
| 222 | ) -> ExtractFilesOutcome |
| 223 | where |
| 224 | F: Fn(usize, usize, &str) + Send + Sync + 'static, |
| 225 | { |
| 226 | let total = files.len(); |
| 227 | let queue: Arc<Mutex<VecDeque<String>>> = Arc::new(Mutex::new(files.into_iter().collect())); |
| 228 | let results: Arc<Mutex<Vec<ExtractTuple>>> = |
| 229 | Arc::new(Mutex::new(Vec::with_capacity(total))); |
| 230 | let skipped: Arc<Mutex<Vec<(String, String)>>> = Arc::new(Mutex::new(Vec::new())); |
| 231 | let progress_count = Arc::new(AtomicUsize::new(0)); |
| 232 | let on_progress = Arc::new(on_progress); |
| 233 | |
| 234 | let handles: Vec<_> = self |
| 235 | .workers |
| 236 | .into_iter() |
| 237 | .map(|worker| { |
| 238 | let queue = queue.clone(); |
| 239 | let results = results.clone(); |
| 240 | let skipped = skipped.clone(); |
| 241 | let progress_count = progress_count.clone(); |
| 242 | let on_progress = on_progress.clone(); |
| 243 | let project_root = self.project_root.clone(); |
| 244 | let self_path = self.self_path.clone(); |
| 245 | let token = self.token; |
| 246 | |
| 247 | std::thread::spawn(move || { |
| 248 | worker_thread( |
| 249 | worker, |
| 250 | queue, |
| 251 | results, |
| 252 | skipped, |
| 253 | progress_count, |
| 254 | on_progress, |
| 255 | project_root, |
| 256 | self_path, |
| 257 | token, |
| 258 | total, |
| 259 | per_file_timeout, |
| 260 | ); |
| 261 | }) |
| 262 | }) |
| 263 | .collect(); |
| 264 | |
| 265 | for h in handles { |
| 266 | let _ = h.join(); |
| 267 | } |
| 268 | |
| 269 | // All worker threads have joined, so we hold the only Arc strong |
| 270 | // reference. `into_inner` returns `Some` in that case; if it ever |
| 271 | // returns `None` (concurrent leak), prefer an empty result over a |
| 272 | // panic — the sync continues and the user just sees zero changes. |
| 273 | let results = Arc::into_inner(results) |
| 274 | .and_then(|m| m.into_inner().ok()) |
no test coverage detected