tidy/
walk.rs

1use std::ffi::OsStr;
2use std::fs::File;
3use std::io::Read;
4use std::path::Path;
5
6use ignore::DirEntry;
7
8/// The default directory filter.
9pub fn filter_dirs(path: &Path) -> bool {
10    // bootstrap/etc
11    let skip = [
12        "tidy-test-file",
13        "compiler/rustc_codegen_cranelift",
14        "compiler/rustc_codegen_gcc",
15        "src/llvm-project",
16        "library/backtrace",
17        "library/portable-simd",
18        "library/stdarch",
19        "src/tools/cargo",
20        "src/tools/clippy",
21        "src/tools/libcxx-version",
22        "src/tools/miri",
23        "src/tools/rust-analyzer",
24        "src/tools/rustc-perf",
25        "src/tools/rustfmt",
26        "src/tools/enzyme",
27        "src/doc/book",
28        "src/doc/edition-guide",
29        "src/doc/embedded-book",
30        "src/doc/nomicon",
31        "src/doc/rust-by-example",
32        "src/doc/rustc-dev-guide",
33        "src/doc/reference",
34        "src/gcc",
35        // Filter RLS output directories
36        "target/rls",
37        "src/bootstrap/target",
38        "vendor",
39    ];
40    skip.iter().any(|p| path.ends_with(p))
41}
42
43/// Filter for only files that end in `.rs`.
44pub fn filter_not_rust(path: &Path) -> bool {
45    path.extension() != Some(OsStr::new("rs")) && !path.is_dir()
46}
47
48pub fn walk(
49    path: &Path,
50    skip: impl Send + Sync + 'static + Fn(&Path, bool) -> bool,
51    f: &mut dyn FnMut(&DirEntry, &str),
52) {
53    walk_many(&[path], skip, f);
54}
55
56pub fn walk_many(
57    paths: &[&Path],
58    skip: impl Send + Sync + 'static + Fn(&Path, bool) -> bool,
59    f: &mut dyn FnMut(&DirEntry, &str),
60) {
61    let mut contents = Vec::new();
62    walk_no_read(paths, skip, &mut |entry| {
63        contents.clear();
64        let mut file = t!(File::open(entry.path()), entry.path());
65        t!(file.read_to_end(&mut contents), entry.path());
66        let contents_str = match std::str::from_utf8(&contents) {
67            Ok(s) => s,
68            Err(_) => return, // skip this file
69        };
70        f(&entry, &contents_str);
71    });
72}
73
74pub(crate) fn walk_no_read(
75    paths: &[&Path],
76    skip: impl Send + Sync + 'static + Fn(&Path, bool) -> bool,
77    f: &mut dyn FnMut(&DirEntry),
78) {
79    let mut walker = ignore::WalkBuilder::new(paths[0]);
80    for path in &paths[1..] {
81        walker.add(path);
82    }
83    let walker = walker.filter_entry(move |e| {
84        !skip(e.path(), e.file_type().map(|ft| ft.is_dir()).unwrap_or(false))
85    });
86    for entry in walker.build().flatten() {
87        if entry.file_type().map_or(true, |kind| kind.is_dir() || kind.is_symlink()) {
88            continue;
89        }
90        f(&entry);
91    }
92}
93
94// Walk through directories and skip symlinks.
95pub(crate) fn walk_dir(
96    path: &Path,
97    skip: impl Send + Sync + 'static + Fn(&Path) -> bool,
98    f: &mut dyn FnMut(&DirEntry),
99) {
100    let mut walker = ignore::WalkBuilder::new(path);
101    let walker = walker.filter_entry(move |e| !skip(e.path()));
102    for entry in walker.build().flatten() {
103        if entry.path().is_dir() {
104            f(&entry);
105        }
106    }
107}