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