Skip to main content

rs_hack/
files.rs

1//! File discovery: glob/dir traversal and exclusion filtering for `.rs` files,
2//! plus the kind→node-type expansion used by `find` and friends.
3
4use std::path::PathBuf;
5
6use anyhow::{Context, Result};
7use glob::glob;
8use walkdir::WalkDir;
9
10pub fn collect_rust_files(paths: &[PathBuf]) -> Result<Vec<PathBuf>> {
11    collect_rust_files_with_exclusions(paths, &[])
12}
13
14pub fn collect_rust_files_with_exclusions(
15    paths: &[PathBuf],
16    exclude_patterns: &[String],
17) -> Result<Vec<PathBuf>> {
18    let mut files = Vec::new();
19
20    for path in paths {
21        let path_str = path.to_string_lossy();
22
23        if path_str.contains('*') || path_str.contains('?') || path_str.contains('[') {
24            for entry in glob(&path_str).context("Failed to parse glob pattern")? {
25                match entry {
26                    Ok(file_path) => {
27                        if file_path.is_file()
28                            && file_path.extension().and_then(|s| s.to_str()) == Some("rs")
29                        {
30                            files.push(file_path);
31                        }
32                    }
33                    Err(e) => eprintln!("Warning: Error reading glob entry: {}", e),
34                }
35            }
36        } else if path.is_file() {
37            if path.extension().and_then(|s| s.to_str()) == Some("rs") {
38                files.push(path.clone());
39            }
40        } else if path.is_dir() {
41            for entry in WalkDir::new(path)
42                .into_iter()
43                .filter_map(|e| e.ok())
44                .filter(|e| e.path().extension().and_then(|s| s.to_str()) == Some("rs"))
45            {
46                files.push(entry.path().to_path_buf());
47            }
48        }
49    }
50
51    if !exclude_patterns.is_empty() {
52        files.retain(|file| {
53            let file_str = file.to_string_lossy();
54            !exclude_patterns.iter().any(|pattern| {
55                if pattern.contains('*') || pattern.contains('?') || pattern.contains('[') {
56                    glob::Pattern::new(pattern)
57                        .map(|p| p.matches(&file_str))
58                        .unwrap_or(false)
59                } else {
60                    file_str.contains(pattern.as_str())
61                }
62            })
63        });
64    }
65
66    Ok(files)
67}
68
69pub fn expand_kind_to_node_types(kind: &str) -> Vec<&'static str> {
70    match kind {
71        "struct" => vec!["struct", "struct-literal"],
72        "function" => vec!["function", "function-call", "method-call", "impl-method", "trait-method"],
73        "enum" => vec!["enum", "enum-usage"],
74        "match" => vec!["match-arm"],
75        "identifier" => vec!["identifier"],
76        "type" => vec!["type-ref", "type-alias"],
77        "macro" => vec!["macro-call"],
78        "const" => vec!["const", "static"],
79        "trait" => vec!["trait", "trait-impl"],
80        "mod" => vec!["mod"],
81        "use" => vec!["use"],
82        _ => vec![],
83    }
84}