claude_native/scan/
classifiers.rs1use std::path::Path;
2
3use crate::scan::ManifestKind;
4
5const SKIP_DIRS: &[&str] = &[
8 ".git", "node_modules", "target", ".next", "dist", "build",
9 "__pycache__", ".venv", "venv", "vendor", ".dart_tool",
10 ".gradle", "Pods", "DerivedData", ".build", ".expo",
11 ".aws-sam", ".serverless", ".terraform", "Library",
12 ".godot", ".import", "coverage", ".turbo", ".nuxt",
13 ".angular", ".cache",
14];
15
16pub fn should_skip_dir(name: &str) -> bool {
17 SKIP_DIRS.contains(&name)
18}
19
20pub fn is_source_extension(ext: &str) -> bool {
21 matches!(ext,
22 "rs" | "ts" | "tsx" | "js" | "jsx" | "mjs" | "cjs" |
23 "py" | "go" | "dart" | "swift" | "kt" | "kts" | "java" |
24 "rb" | "cs" | "ex" | "exs" | "cpp" | "cc" | "c" | "h" | "hpp" |
25 "vue" | "svelte" | "tf" | "hcl" | "yaml" | "yml" | "toml" | "json" |
26 "graphql" | "proto" | "prisma" | "sql" | "sh" | "bash" | "zsh" |
27 "md" | "mdx" | "css" | "scss" | "less" | "html" | "xml"
28 )
29}
30
31pub fn is_test_file(path: &Path) -> bool {
32 let name = path.file_stem()
33 .and_then(|s| s.to_str())
34 .unwrap_or("");
35 let full = path.to_string_lossy();
36
37 name.ends_with("_test")
38 || name.ends_with(".test")
39 || name.ends_with("_spec")
40 || name.ends_with(".spec")
41 || name.starts_with("test_")
42 || full.contains("__tests__")
43 || full.contains("/tests/")
44 || full.contains("/test/")
45 || full.contains("/spec/")
46}
47
48pub fn is_generated_file(path: &Path) -> bool {
49 let full = path.to_string_lossy();
50 let name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
51
52 full.contains("/generated/")
53 || full.contains("/gen/")
54 || name.contains(".g.")
55 || name.contains(".generated.")
56 || name.contains(".freezed.")
57 || name.contains(".pb.")
58 || name == "R.java"
59 || name == "R.kt"
60 || name == "BuildConfig.kt"
61 || name == "BuildConfig.java"
62}
63
64pub fn is_lock_file(name: &str) -> bool {
65 matches!(name,
66 "package-lock.json" | "yarn.lock" | "pnpm-lock.yaml" |
67 "Cargo.lock" | "Gemfile.lock" | "poetry.lock" | "go.sum" |
68 "composer.lock" | "pubspec.lock" | "Pipfile.lock"
69 )
70}
71
72pub fn is_ci_config(path: &Path) -> bool {
73 let full = path.to_string_lossy();
74 let name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
75
76 full.contains(".github/workflows")
77 || name == ".gitlab-ci.yml"
78 || name == "Jenkinsfile"
79 || name == ".circleci"
80 || name == ".travis.yml"
81 || name == "azure-pipelines.yml"
82 || name == "bitbucket-pipelines.yml"
83}
84
85pub fn is_env_file(name: &str) -> bool {
86 name == ".env"
87 || name.starts_with(".env.")
88 || name.ends_with(".env")
89}
90
91pub fn is_manifest(name: &str) -> Option<ManifestKind> {
92 match name {
93 "package.json" => Some(ManifestKind::PackageJson),
94 "Cargo.toml" => Some(ManifestKind::CargoToml),
95 "go.mod" => Some(ManifestKind::GoMod),
96 "pubspec.yaml" => Some(ManifestKind::PubspecYaml),
97 "requirements.txt" => Some(ManifestKind::RequirementsTxt),
98 "pyproject.toml" => Some(ManifestKind::PyprojectToml),
99 "Gemfile" => Some(ManifestKind::Gemfile),
100 "mix.exs" => Some(ManifestKind::MixExs),
101 _ => None,
102 }
103}