use std::path::Path;
use crate::detector::{DetectMatch, Detector, DetectorRegistry};
use crate::kind::KindId;
fn signal_if_exists(dir: &Path, name: &str, out: &mut Vec<String>) -> bool {
if dir.join(name).exists() {
out.push(name.to_string());
true
} else {
false
}
}
fn any_exists(dir: &Path, names: &[&str], out: &mut Vec<String>) -> bool {
let mut found = false;
for n in names {
if signal_if_exists(dir, n, out) {
found = true;
}
}
found
}
fn has_extension(dir: &Path, ext: &str) -> bool {
let Ok(read) = std::fs::read_dir(dir) else {
return false;
};
for entry in read.flatten() {
if entry.path().extension().map(|e| e == ext).unwrap_or(false) {
return true;
}
}
false
}
#[derive(Default)]
pub struct RustDetector;
impl Detector for RustDetector {
fn kind(&self) -> KindId { KindId::RUST }
fn priority(&self) -> i32 { 100 }
fn detect(&self, dir: &Path) -> Option<DetectMatch> {
let mut signals = Vec::new();
if signal_if_exists(dir, "Cargo.toml", &mut signals) {
Some(DetectMatch { kind: KindId::RUST, signals })
} else {
None
}
}
}
#[derive(Default)]
pub struct BunDetector;
impl Detector for BunDetector {
fn kind(&self) -> KindId { KindId::BUN }
fn priority(&self) -> i32 { 80 }
fn detect(&self, dir: &Path) -> Option<DetectMatch> {
let mut signals = Vec::new();
if any_exists(dir, &["bun.lock", "bun.lockb", "bunfig.toml"], &mut signals) {
Some(DetectMatch { kind: KindId::BUN, signals })
} else {
None
}
}
}
#[derive(Default)]
pub struct DenoDetector;
impl Detector for DenoDetector {
fn kind(&self) -> KindId { KindId::DENO }
fn priority(&self) -> i32 { 70 }
fn detect(&self, dir: &Path) -> Option<DetectMatch> {
let mut signals = Vec::new();
if any_exists(dir, &["deno.json", "deno.jsonc", "deno.lock"], &mut signals) {
Some(DetectMatch { kind: KindId::DENO, signals })
} else {
None
}
}
}
#[derive(Default)]
pub struct NodeDetector;
impl Detector for NodeDetector {
fn kind(&self) -> KindId { KindId::NODE }
fn priority(&self) -> i32 { 50 }
fn detect(&self, dir: &Path) -> Option<DetectMatch> {
let mut signals = Vec::new();
if signal_if_exists(dir, "package.json", &mut signals) {
Some(DetectMatch { kind: KindId::NODE, signals })
} else {
None
}
}
}
#[derive(Default)]
pub struct PythonDetector;
impl Detector for PythonDetector {
fn kind(&self) -> KindId { KindId::PYTHON }
fn priority(&self) -> i32 { 40 }
fn detect(&self, dir: &Path) -> Option<DetectMatch> {
let mut signals = Vec::new();
if any_exists(
dir,
&["pyproject.toml", "requirements.txt", "setup.py", "setup.cfg"],
&mut signals,
) {
Some(DetectMatch { kind: KindId::PYTHON, signals })
} else {
None
}
}
}
#[derive(Default)]
pub struct LuaDetector;
impl Detector for LuaDetector {
fn kind(&self) -> KindId { KindId::LUA }
fn priority(&self) -> i32 { 30 }
fn detect(&self, dir: &Path) -> Option<DetectMatch> {
let mut signals = Vec::new();
let luarc = signal_if_exists(dir, ".luarc.json", &mut signals);
let rockspec = has_extension(dir, "rockspec");
if rockspec {
signals.push("*.rockspec".to_string());
}
if luarc || rockspec {
Some(DetectMatch { kind: KindId::LUA, signals })
} else {
None
}
}
}
#[derive(Default)]
pub struct CppDetector;
impl Detector for CppDetector {
fn kind(&self) -> KindId { KindId::CPP }
fn priority(&self) -> i32 { 20 }
fn detect(&self, dir: &Path) -> Option<DetectMatch> {
let mut signals = Vec::new();
if !signal_if_exists(dir, "CMakeLists.txt", &mut signals) {
return None;
}
if has_extension(dir, "cpp") || has_extension(dir, "cc") || has_extension(dir, "cxx") {
Some(DetectMatch { kind: KindId::CPP, signals })
} else {
None
}
}
}
#[derive(Default)]
pub struct CDetector;
impl Detector for CDetector {
fn kind(&self) -> KindId { KindId::C }
fn priority(&self) -> i32 { 10 }
fn detect(&self, dir: &Path) -> Option<DetectMatch> {
let mut signals = Vec::new();
if !signal_if_exists(dir, "CMakeLists.txt", &mut signals) {
return None;
}
if has_extension(dir, "cpp") || has_extension(dir, "cc") || has_extension(dir, "cxx") {
return None;
}
if has_extension(dir, "c") {
Some(DetectMatch { kind: KindId::C, signals })
} else {
None
}
}
}
pub fn register_all(registry: &mut DetectorRegistry) {
registry
.add(RustDetector)
.add(BunDetector)
.add(DenoDetector)
.add(NodeDetector)
.add(PythonDetector)
.add(LuaDetector)
.add(CppDetector)
.add(CDetector);
}