Skip to main content

_diffctx/
candidate_files.rs

1use std::path::{Path, PathBuf};
2
3use rayon::prelude::*;
4use rustc_hash::FxHashSet;
5use walkdir::WalkDir;
6
7use crate::config::graph_filtering::GRAPH_FILTERING;
8use crate::config::limits::LIMITS;
9use crate::git;
10use crate::languages::get_language_for_file;
11
12fn is_allowed_file(path: &Path) -> bool {
13    get_language_for_file(&path.to_string_lossy()).is_some()
14}
15
16fn is_candidate_file(
17    file_path: &Path,
18    _root_dir: &Path,
19    included_set: &FxHashSet<PathBuf>,
20) -> bool {
21    if !file_path.is_file() {
22        return false;
23    }
24    if !is_allowed_file(file_path) {
25        return false;
26    }
27    if included_set.contains(file_path) {
28        return false;
29    }
30    match file_path.metadata() {
31        Ok(meta) if meta.len() as usize > LIMITS.max_file_size => return false,
32        Err(_) => return false,
33        _ => {}
34    }
35    true
36}
37
38pub fn collect_candidate_files(root_dir: &Path, included_set: &FxHashSet<PathBuf>) -> Vec<PathBuf> {
39    if let Ok(parts) = git::run_git_z(root_dir, &["ls-files", "-z"]) {
40        let all_paths: Vec<PathBuf> = parts.into_iter().map(|f| root_dir.join(f)).collect();
41        let files: Vec<PathBuf> = all_paths
42            .into_par_iter()
43            .filter(|f| is_candidate_file(f, root_dir, included_set))
44            .collect();
45        return files;
46    }
47
48    let mut fallback: Vec<PathBuf> = Vec::new();
49    for entry in WalkDir::new(root_dir)
50        .sort_by_file_name()
51        .into_iter()
52        .filter_entry(|e| {
53            if e.depth() == 0 || !e.file_type().is_dir() {
54                return true;
55            }
56            match e.file_name().to_str() {
57                Some(name) => {
58                    !name.starts_with('.') && name != "node_modules" && name != "__pycache__"
59                }
60                None => true,
61            }
62        })
63        .filter_map(|e| e.ok())
64    {
65        if !entry.file_type().is_file() {
66            continue;
67        }
68        if fallback.len() >= GRAPH_FILTERING.fallback_max_files {
69            break;
70        }
71        let path = entry.into_path();
72        if is_candidate_file(&path, root_dir, included_set) {
73            fallback.push(path);
74        }
75    }
76    fallback
77}
78
79pub fn normalize_path(path: &Path, root_dir: &Path) -> PathBuf {
80    if path.is_absolute() {
81        path.canonicalize().unwrap_or_else(|_| path.to_path_buf())
82    } else {
83        let joined = root_dir.join(path);
84        joined.canonicalize().unwrap_or(joined)
85    }
86}