Skip to main content

stern4rust/
source_walker.rs

1// Copyright 2025 Umberto Gotti <umberto.gotti@umbertogotti.dev>
2// Licensed under the MIT License
3// SPDX-License-Identifier: MIT
4
5use std::path::Path;
6use std::path::PathBuf;
7
8use walkdir::WalkDir;
9
10// Every .rs file under a package, tests included.
11//
12// "Every .rs file" is the rule's own wording, so src/ and tests/ are both in
13// scope -- a test file without the header is exactly as wrong as a source file
14// without it. target/ is skipped because it holds generated code nobody wrote
15// and build scripts write files there that no rule should judge.
16//
17// A directory holding its own Cargo.toml is skipped too, because it is a
18// different package. Its files are that package's to answer for, under whatever
19// rules it has chosen, and cargo would not compile them as part of this one
20// either. The shape this was written for is a fixture crate under
21// tests/fixtures/ -- sample code a tool analyses rather than code it ships.
22pub struct SourceWalker;
23
24impl SourceWalker {
25    pub fn walk(root: &Path) -> Vec<PathBuf> {
26        let mut paths: Vec<PathBuf> = WalkDir::new(root)
27            .into_iter()
28            .filter_entry(|entry| !Self::is_skipped(root, entry.path()))
29            .filter_map(Result::ok)
30            .filter(|entry| entry.file_type().is_file())
31            .map(|entry| entry.into_path())
32            .filter(|path| path.extension().is_some_and(|extension| extension == "rs"))
33            .collect();
34        paths.sort();
35        paths
36    }
37
38    // The package being walked holds a manifest by definition, so it has to be
39    // exempt from the nested-package rule. Without this the walk skips the root
40    // and every run reports a clean tree.
41    fn is_skipped(root: &Path, path: &Path) -> bool {
42        if path == root {
43            return false;
44        }
45        matches!(
46            path.file_name().and_then(|name| name.to_str()),
47            Some("target") | Some(".git")
48        ) || Self::is_another_package(path)
49    }
50
51    fn is_another_package(path: &Path) -> bool {
52        path.is_dir() && path.join("Cargo.toml").is_file()
53    }
54}