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 {
19 Regex::new(
21 r"(?s)(printf|fprintf|sprintf|snprintf|printf_debug|dprintf|puts|fputs|fputc|putchar|fputchar|write|perror)\s*\([^)]*\)\s*;",
22 )?
23 } else {
24 Regex::new(
26 r"(?s)(printf|fprintf|sprintf|snprintf|printf_debug|dprintf|puts|fputs|fputc|putchar|fputchar|write|perror)\s*\([^)]*?(debug|DEBUG)[^)]*\)\s*;",
27 )?
28 };
29
30 let cpp_stream_pattern = if detect_all {
32 Regex::new(r"(?s)(std::cout|std::cerr|std::clog)\s*<<[^;]*?;")?
34 } else {
35 Regex::new(r"(?s)(std::cout|std::cerr|std::clog)\s*<<[^;]*?(debug|DEBUG)[^;]*?;")?
37 };
38
39 let rust_macro_pattern = if detect_all {
42 Regex::new(r"(?s)(println!|eprintln!|print!|eprint!|dbg!)\s*\([^)]*\)\s*;")?
44 } else {
45 Regex::new(
47 r"(?s)dbg!\s*\([^)]*\)\s*;|(println!|eprintln!|print!|eprint!)\s*\([^)]*?(debug|DEBUG)[^)]*\)\s*;",
48 )?
49 };
50
51 let comment_pattern = Regex::new(r"^\s*//")?;
52
53 let entries: Vec<_> = if path.is_file() {
54 vec![path.to_path_buf()]
55 } else {
56 WalkDir::new(path)
57 .into_iter()
58 .filter_map(|e| e.ok())
59 .filter(|e| {
60 e.path().is_file()
61 && e.path()
62 .extension()
63 .and_then(|s| s.to_str())
64 .map(|ext| matches!(ext, "c" | "h" | "cpp" | "hpp" | "cc" | "cxx" | "rs"))
65 .unwrap_or(false)
66 })
67 .map(|e| e.path().to_path_buf())
68 .collect()
69 };
70
71 for file_path in entries {
72 let content = fs::read_to_string(&file_path)
73 .with_context(|| format!("Failed to read file: {}", file_path.display()))?;
74
75 for cap in c_functions_pattern.find_iter(&content) {
77 let match_str = cap.as_str();
78 let start_offset = cap.start();
79 let end_offset = cap.end();
80
81 let line_number = content[..start_offset].matches('\n').count() + 1;
84 let end_line_number = content[..end_offset].matches('\n').count() + 1;
86
87 let line_start_offset = content[..start_offset]
89 .rfind('\n')
90 .map(|pos| pos + 1)
91 .unwrap_or(0);
92 let line_content = content[line_start_offset..]
93 .lines()
94 .next()
95 .unwrap_or("")
96 .to_string();
97
98 let is_commented = comment_pattern.is_match(&line_content);
100
101 if is_commented == find_commented {
102 let multiline_content: Vec<String> =
104 match_str.lines().map(|s| s.to_string()).collect();
105
106 matches.push(Match {
107 file_path: file_path.clone(),
108 line_number,
109 end_line_number,
110 line_content: match_str.replace('\n', " ").trim().to_string(),
111 multiline_content,
112 });
113 }
114 }
115
116 for cap in cpp_stream_pattern.find_iter(&content) {
118 let match_str = cap.as_str();
119 let start_offset = cap.start();
120 let end_offset = cap.end();
121
122 let line_number = content[..start_offset].matches('\n').count() + 1;
125 let end_line_number = content[..end_offset].matches('\n').count() + 1;
127
128 let line_start_offset = content[..start_offset]
130 .rfind('\n')
131 .map(|pos| pos + 1)
132 .unwrap_or(0);
133 let line_content = content[line_start_offset..]
134 .lines()
135 .next()
136 .unwrap_or("")
137 .to_string();
138
139 let is_commented = comment_pattern.is_match(&line_content);
141
142 if is_commented == find_commented {
143 let multiline_content: Vec<String> =
145 match_str.lines().map(|s| s.to_string()).collect();
146
147 matches.push(Match {
148 file_path: file_path.clone(),
149 line_number,
150 end_line_number,
151 line_content: match_str.replace('\n', " ").trim().to_string(),
152 multiline_content,
153 });
154 }
155 }
156
157 for cap in rust_macro_pattern.find_iter(&content) {
159 let match_str = cap.as_str();
160 let start_offset = cap.start();
161 let end_offset = cap.end();
162
163 let line_number = content[..start_offset].matches('\n').count() + 1;
165 let end_line_number = content[..end_offset].matches('\n').count() + 1;
166
167 let line_start_offset = content[..start_offset]
169 .rfind('\n')
170 .map(|pos| pos + 1)
171 .unwrap_or(0);
172 let line_content = content[line_start_offset..]
173 .lines()
174 .next()
175 .unwrap_or("")
176 .to_string();
177
178 let is_commented = comment_pattern.is_match(&line_content);
180
181 if is_commented == find_commented {
182 let multiline_content: Vec<String> =
184 match_str.lines().map(|s| s.to_string()).collect();
185
186 matches.push(Match {
187 file_path: file_path.clone(),
188 line_number,
189 end_line_number,
190 line_content: match_str.replace('\n', " ").trim().to_string(),
191 multiline_content,
192 });
193 }
194 }
195 }
196
197 Ok(matches)
198}