1use anyhow::{Context, Result};
2use regex::Regex;
3use std::fs;
4use std::path::Path;
5use walkdir::WalkDir;
6
7use crate::types::Match;
8
9pub fn find_debug_printfs(
10 path: &Path,
11 find_commented: bool,
12 detect_all: bool,
13) -> Result<Vec<Match>> {
14 let mut matches = Vec::new();
15
16 let c_functions_pattern = if detect_all {
18 Regex::new(
20 r"(printf|fprintf|sprintf|snprintf|printf_debug|dprintf|puts|fputs|fputc|putchar|fputchar|write|perror)\s*\([^;]*?;",
21 )?
22 } else {
23 Regex::new(
25 r"(printf|fprintf|sprintf|snprintf|printf_debug|dprintf|puts|fputs|fputc|putchar|fputchar|write|perror)\s*\([^;]*?(debug|DEBUG)[^;]*?;",
26 )?
27 };
28
29 let cpp_stream_pattern = if detect_all {
31 Regex::new(r"(std::cout|std::cerr|std::clog)\s*<<[^;]*?;")?
33 } else {
34 Regex::new(r"(std::cout|std::cerr|std::clog)\s*<<[^;]*?(debug|DEBUG)[^;]*?;")?
36 };
37
38 let comment_pattern = Regex::new(r"^\s*//")?;
39
40 let entries: Vec<_> = if path.is_file() {
41 vec![path.to_path_buf()]
42 } else {
43 WalkDir::new(path)
44 .into_iter()
45 .filter_map(|e| e.ok())
46 .filter(|e| {
47 e.path().is_file()
48 && e.path()
49 .extension()
50 .and_then(|s| s.to_str())
51 .map(|ext| matches!(ext, "c" | "h" | "cpp" | "hpp" | "cc" | "cxx"))
52 .unwrap_or(false)
53 })
54 .map(|e| e.path().to_path_buf())
55 .collect()
56 };
57
58 for file_path in entries {
59 let content = fs::read_to_string(&file_path)
60 .with_context(|| format!("Failed to read file: {}", file_path.display()))?;
61
62 for (line_num, line) in content.lines().enumerate() {
63 let is_commented = comment_pattern.is_match(line);
64 let has_debug_output =
65 c_functions_pattern.is_match(line) || cpp_stream_pattern.is_match(line);
66
67 if has_debug_output && is_commented == find_commented {
69 matches.push(Match {
70 file_path: file_path.clone(),
71 line_number: line_num + 1,
72 line_content: line.to_string(),
73 });
74 }
75 }
76 }
77
78 Ok(matches)
79}