use crate::audits::context::model::LanguageKind;
use std::path::Path;
pub fn push_unique<T: PartialEq>(values: &mut Vec<T>, value: T) {
if !values.contains(&value) {
values.push(value);
}
}
pub fn normalize(value: &str) -> String {
value.trim().to_lowercase()
}
pub fn path_contains_component(path: &Path, targets: &[&str]) -> bool {
path.components().any(|component| {
component
.as_os_str()
.to_str()
.map(|value| {
let normalized = normalize(value);
targets.iter().any(|target| normalized == *target)
})
.unwrap_or(false)
})
}
pub fn is_pascal_case(value: &str) -> bool {
value
.chars()
.next()
.map(|character| character.is_uppercase())
.unwrap_or(false)
}
pub fn is_js_or_ts(language: LanguageKind) -> bool {
matches!(
language,
LanguageKind::TypeScript | LanguageKind::JavaScript
)
}
pub fn is_test_file(path: &Path, has_inline_tests: bool) -> bool {
if has_inline_tests {
return true;
}
let path_text = path.to_string_lossy().to_lowercase();
let file_name = path
.file_name()
.and_then(|name| name.to_str())
.map(|name| name.to_lowercase())
.unwrap_or_default();
path_text.starts_with("tests/")
|| path_text.contains("/tests/")
|| path_text.contains("\\tests\\")
|| path_text.contains("/__tests__/")
|| path_text.contains("\\__tests__\\")
|| file_name.ends_with(".test.ts")
|| file_name.ends_with(".test.tsx")
|| file_name.ends_with(".test.js")
|| file_name.ends_with(".test.jsx")
|| file_name.ends_with(".spec.ts")
|| file_name.ends_with(".spec.tsx")
|| file_name.ends_with(".spec.js")
|| file_name.ends_with(".spec.jsx")
|| file_name.ends_with("_test.rs")
|| file_name.ends_with("_test.go")
|| file_name.ends_with("_test.py")
|| file_name.starts_with("test_")
|| file_name.ends_with("test.java")
|| file_name.ends_with("tests.java")
|| file_name.ends_with("test.kt")
|| file_name.ends_with("tests.kt")
|| file_name.ends_with("test.cs")
|| file_name.ends_with("tests.cs")
}
pub fn is_config_file(path: &Path) -> bool {
let file_name = path
.file_name()
.and_then(|name| name.to_str())
.map(normalize)
.unwrap_or_default();
matches!(
file_name.as_str(),
"package.json"
| "tsconfig.json"
| "vite.config.ts"
| "vite.config.js"
| "next.config.js"
| "next.config.mjs"
| "cargo.toml"
| "cargo.lock"
| "projectsettings.asset"
| "dockerfile"
| "containerfile"
| "go.mod"
| "go.sum"
| "pyproject.toml"
| "requirements.txt"
| "build.gradle"
| "settings.gradle"
| "pom.xml"
) || (file_name.starts_with("appsettings") && file_name.ends_with(".json"))
}
pub fn is_generated_file(path: &Path, content: &str) -> bool {
path_contains_component(
path,
&[
"generated",
"__generated__",
"gen",
"codegen",
"target",
"build",
],
) || content.contains("@generated")
|| content.contains("code generated")
|| content.contains("Code generated")
|| content.contains("Code Generated")
|| content.contains("generated by")
|| content.contains("Generated by")
|| content.contains("Generated By")
}
pub fn is_app_entrypoint(path: &Path, content: &str, language: LanguageKind) -> bool {
let file_name = path
.file_name()
.and_then(|name| name.to_str())
.map(normalize)
.unwrap_or_default();
matches!(
file_name.as_str(),
"main.rs"
| "main.go"
| "main.py"
| "app.py"
| "program.cs"
| "main.java"
| "main.kt"
| "index.ts"
| "index.js"
| "main.ts"
| "main.js"
) || (language == LanguageKind::Python && content.contains("if __name__ == \"__main__\""))
|| (language == LanguageKind::Go && content.contains("func main("))
|| (language == LanguageKind::Rust && content.contains("fn main("))
}