fn detect_bin_targets(entry_points: &mut Vec<String>) {
if let Ok(entries) = std::fs::read_dir("src/bin") {
for entry in entries.flatten() {
if let Some(name) = entry.path().file_stem() {
entry_points.push(format!("bin/{}", name.to_string_lossy()));
}
}
}
}
fn detect_wasm_entry_points(entry_points: &mut Vec<String>) {
if !Path::new("Cargo.toml").exists() {
return;
}
if let Ok(content) = std::fs::read_to_string("Cargo.toml") {
if content.contains("wasm-bindgen") || content.contains("wasm-pack") {
entry_points.push("wasm_bindgen".into());
}
}
}
fn detect_ffi_entry_points(entry_points: &mut Vec<String>) {
let Ok(entries) = std::fs::read_dir("src") else {
return;
};
for entry in entries.flatten() {
if let Ok(content) = std::fs::read_to_string(entry.path()) {
if content.contains("#[no_mangle]") {
entry_points.push("no_mangle".into());
return;
}
}
}
}
impl DeepContextConfig {
pub fn validate(&self) -> Result<(), Vec<String>> {
let mut errors = Vec::new();
if self.entry_points.is_empty() {
let detected = self.detect_entry_points();
if detected.is_empty() {
errors.push("No entry points configured or detected".into());
}
} else {
let has_standard = self.entry_points.iter().any(|ep| {
ep == "main"
|| ep.ends_with("::main")
|| ep == "lib"
|| ep.starts_with("bin/")
|| ep.contains("wasm_bindgen")
|| ep.contains("no_mangle")
});
if !has_standard {
errors.push(
"No standard entry point found (main, lib, bin/*, wasm_bindgen, no_mangle). \
This may cause false dead code positives."
.into(),
);
}
}
if self.dead_code_threshold < 0.0 || self.dead_code_threshold > 1.0 {
errors.push(format!(
"Invalid dead_code_threshold: {} (must be 0.0-1.0)",
self.dead_code_threshold
));
}
if self.complexity_thresholds.cyclomatic_warning
>= self.complexity_thresholds.cyclomatic_error
{
errors.push(format!(
"Cyclomatic warning threshold ({}) must be less than error threshold ({})",
self.complexity_thresholds.cyclomatic_warning,
self.complexity_thresholds.cyclomatic_error
));
}
if self.complexity_thresholds.cognitive_warning
>= self.complexity_thresholds.cognitive_error
{
errors.push(format!(
"Cognitive warning threshold ({}) must be less than error threshold ({})",
self.complexity_thresholds.cognitive_warning,
self.complexity_thresholds.cognitive_error
));
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
#[must_use]
pub fn detect_entry_points(&self) -> Vec<String> {
let mut entry_points = Vec::new();
if Path::new("src/main.rs").exists() {
entry_points.push("main".into());
}
if Path::new("src/lib.rs").exists() {
entry_points.push("lib".into());
}
detect_bin_targets(&mut entry_points);
detect_wasm_entry_points(&mut entry_points);
detect_ffi_entry_points(&mut entry_points);
entry_points
}
pub fn merge_with_detected(&mut self) {
if self.entry_points.is_empty() {
self.entry_points = self.detect_entry_points();
} else {
let detected = self.detect_entry_points();
for ep in detected {
if !self.entry_points.contains(&ep) {
self.entry_points.push(ep);
}
}
}
}
pub fn load_from_file(path: &Path) -> Result<Self, Box<dyn std::error::Error>> {
let content = std::fs::read_to_string(path)?;
let mut config: Self = toml::from_str(&content)?;
if let Err(errors) = config.validate() {
return Err(errors.join("; ").into());
}
config.merge_with_detected();
Ok(config)
}
pub fn save_to_file(&self, path: &Path) -> Result<(), Box<dyn std::error::Error>> {
let content = toml::to_string_pretty(self)?;
std::fs::write(path, content)?;
Ok(())
}
}