1use ignore::overrides::OverrideBuilder;
9use ignore::{WalkBuilder, WalkState};
10use std::fs::Metadata;
11use std::path::PathBuf;
12use std::sync::mpsc::channel;
13
14use crate::config::Config;
15use crate::error::{Error, Result};
16use crate::paths::Paths;
17
18#[derive(Debug, Clone)]
20pub struct WalkEntry {
21 pub path: PathBuf,
23 pub rel: String,
25 pub metadata: Metadata,
26}
27
28pub fn walk(paths: &Paths, config: &Config) -> Result<Vec<WalkEntry>> {
30 let mut overrides = OverrideBuilder::new(&paths.root);
31 for pat in &config.include {
32 overrides
33 .add(pat)
34 .map_err(|e| Error::other(format!("bad include glob {pat:?}: {e}")))?;
35 }
36 for pat in &config.exclude {
37 overrides
38 .add(&format!("!{pat}"))
39 .map_err(|e| Error::other(format!("bad exclude glob {pat:?}: {e}")))?;
40 }
41 let overrides = overrides
42 .build()
43 .map_err(|e| Error::other(format!("invalid overrides: {e}")))?;
44
45 let mut builder = WalkBuilder::new(&paths.root);
46 builder
47 .hidden(!config.index_hidden)
48 .git_ignore(config.respect_gitignore)
49 .git_global(config.respect_gitignore)
50 .git_exclude(config.respect_gitignore)
51 .ignore(config.respect_gitignore)
52 .parents(config.respect_gitignore)
53 .overrides(overrides)
54 .follow_links(false);
55
56 let (tx, rx) = channel::<WalkEntry>();
59 let root = paths.root.clone();
60 let max_size = config.max_file_size;
61 builder.build_parallel().run(|| {
62 let tx = tx.clone();
63 let root = root.clone();
64 Box::new(move |result| {
65 let dent = match result {
66 Ok(d) => d,
67 Err(_) => return WalkState::Continue,
68 };
69 match dent.file_type() {
70 Some(ft) if ft.is_file() => {}
71 _ => return WalkState::Continue,
72 }
73 let metadata = match dent.metadata() {
74 Ok(m) => m,
75 Err(_) => return WalkState::Continue,
76 };
77 if metadata.len() > max_size || metadata.len() == 0 {
78 return WalkState::Continue;
79 }
80 let path = dent.into_path();
81 let rel = match path.strip_prefix(&root) {
82 Ok(r) => r.to_string_lossy().replace('\\', "/"),
83 Err(_) => path.to_string_lossy().to_string(),
84 };
85 let _ = tx.send(WalkEntry {
86 path,
87 rel,
88 metadata,
89 });
90 WalkState::Continue
91 })
92 });
93 drop(tx);
94 Ok(rx.into_iter().collect())
95}