1use anyhow::Result;
2use clap::{Parser, Subcommand};
3use colored::*;
4use std::path::PathBuf;
5#[derive(Parser, Debug)]
6#[command(name = "sweep")]
7#[command(about = "๐งน Sweep - Debug statement cleaner and code formatter")]
8pub struct SweepCli {
9 #[command(subcommand)]
10 command: SweepCommands,
11 #[arg(short, long)]
12 verbose: bool,
13}
14#[derive(Subcommand, Debug, Clone)]
15pub enum SweepCommands {
16 Scan {
17 #[arg(default_value = ".")]
18 path: PathBuf,
19 #[arg(long)]
20 include_tests: bool,
21 #[arg(long)]
22 include_examples: bool,
23 #[arg(long)]
24 export: Option<PathBuf>,
25 },
26 Sweep {
27 #[arg(default_value = ".")]
28 path: PathBuf,
29 #[arg(long)]
30 dry_run: bool,
31 #[arg(long)]
32 interactive: bool,
33 #[arg(long)]
34 prompt: bool,
35 #[arg(long)]
36 keep_main: bool,
37 #[arg(long)]
38 keep_tests: bool,
39 #[arg(long)]
40 keep_examples: bool,
41 #[arg(long)]
42 backup: bool,
43 #[arg(long)]
44 yes: bool,
45 #[arg(long)]
46 pristine: bool,
47 #[arg(long)]
48 format: bool,
49 #[arg(long)]
50 organize_imports: bool,
51 #[arg(long)]
52 add_docs: bool,
53 #[arg(long)]
54 fix_clippy: bool,
55 },
56 Convert {
57 #[arg(default_value = ".")]
58 path: PathBuf,
59 #[arg(long)]
60 println_level: Option<String>,
61 #[arg(long)]
62 eprintln_level: Option<String>,
63 #[arg(long)]
64 dry_run: bool,
65 #[arg(long)]
66 add_dependency: bool,
67 },
68 Analyze {
69 #[arg(default_value = ".")]
70 path: PathBuf,
71 #[arg(long, default_value = "10")]
72 top: usize,
73 },
74 Init { #[arg(long)] force: bool },
75 Help,
76}
77pub fn run_sweep(command: SweepCommands, verbose: bool) -> Result<()> {
78 match command {
79 SweepCommands::Scan { path, include_tests, include_examples, export } => {
80 println!("๐ Scanning for print statements in: {}", path.display());
81 let sweeper = crate::sweeping::src::away::Sweeper::new();
82 let statements = sweeper
83 .scan_directory(&path, include_tests, include_examples)?;
84 if statements.is_empty() {
85 println!("{}", "โจ Clean! No print statements found.".green());
86 return Ok(());
87 }
88 println!("\n{}\n", "๐ Scan Results".bold().blue());
89 println!("{}", "โ".repeat(60).dimmed());
90 for stmt in &statements {
91 sweeper.display_statement(stmt);
92 }
93 println!("\n{}: {}", "Total".bold(), statements.len());
94 if let Some(export_path) = export {
95 crate::sweeping::src::away::export_to_json(&statements, &export_path)?;
96 println!("๐ Results exported to: {}", export_path.display());
97 }
98 }
99 SweepCommands::Sweep {
100 path,
101 dry_run,
102 interactive,
103 prompt,
104 keep_main,
105 keep_tests,
106 keep_examples,
107 backup,
108 yes,
109 pristine,
110 format,
111 organize_imports,
112 add_docs,
113 fix_clippy,
114 } => {
115 println!("๐งน Cleaning debug statements in: {}", path.display());
116 let sweeper = crate::sweeping::src::away::Sweeper::new();
117 let statements = sweeper.scan_directory(&path, !keep_tests, !keep_examples)?;
118 if statements.is_empty() {
119 println!("{}", "โจ Already clean! No print statements found.".green());
120 return Ok(());
121 }
122 println!("Found {} print statements to process", statements.len());
123 if dry_run {
124 println!("{}", "๐ DRY RUN - Showing what would be changed:".yellow());
125 }
126 let mut sweeper = crate::sweeping::src::away::Sweeper::new();
127 sweeper.load_config(&std::path::PathBuf::from(".sweep.toml"))?;
128 let options = crate::sweeping::src::away::SweepOptions {
129 dry_run,
130 interactive,
131 prompt,
132 keep_main,
133 keep_tests,
134 keep_examples,
135 backup,
136 yes,
137 };
138 sweeper
139 .sweep_files(
140 statements,
141 &options,
142 &std::path::PathBuf::from(".sweep.toml"),
143 )?;
144 }
145 SweepCommands::Convert {
146 path,
147 println_level,
148 eprintln_level,
149 dry_run,
150 add_dependency,
151 } => {
152 println!(
153 "๐ Converting print statements to logging in: {}", path.display()
154 );
155 let sweeper = crate::sweeping::src::away::Sweeper::new();
156 let statements = sweeper.scan_directory(&path, true, true)?;
157 if statements.is_empty() {
158 println!("{}", "โจ No print statements found to convert!".green());
159 return Ok(());
160 }
161 let println_level = println_level
162 .map(|s| {
163 match s.to_lowercase().as_str() {
164 "trace" => crate::sweeping::src::away::LogLevel::Trace,
165 "info" => crate::sweeping::src::away::LogLevel::Info,
166 "warn" => crate::sweeping::src::away::LogLevel::Warn,
167 "error" => crate::sweeping::src::away::LogLevel::Error,
168 _ => crate::sweeping::src::away::LogLevel::Debug,
169 }
170 })
171 .unwrap_or(crate::sweeping::src::away::LogLevel::Debug);
172 let eprintln_level = eprintln_level
173 .map(|s| {
174 match s.to_lowercase().as_str() {
175 "trace" => crate::sweeping::src::away::LogLevel::Trace,
176 "info" => crate::sweeping::src::away::LogLevel::Info,
177 "warn" => crate::sweeping::src::away::LogLevel::Warn,
178 "error" => crate::sweeping::src::away::LogLevel::Error,
179 _ => crate::sweeping::src::away::LogLevel::Error,
180 }
181 })
182 .unwrap_or(crate::sweeping::src::away::LogLevel::Error);
183 let mut converted = 0;
184 let mut files_modified = 0;
185 for stmt in statements {
186 if !dry_run {
187 if let Err(e) = crate::sweeping::src::away::convert_statement_in_file(
188 &stmt.file,
189 &stmt.content,
190 &stmt.content,
191 ) {
192 eprintln!("Error converting {}: {}", stmt.file.display(), e);
193 continue;
194 }
195 converted += 1;
196 files_modified += 1;
197 } else {
198 println!(
199 "Would convert: {} at {}:{}", stmt.file.display(), stmt.line,
200 stmt.column
201 );
202 converted += 1;
203 }
204 }
205 if dry_run {
206 println!(
207 "{} Would convert {} statements across {} files", "๐".yellow(),
208 converted, files_modified
209 );
210 } else {
211 println!(
212 "{} Converted {} statements across {} files", "โ
".green(),
213 converted, files_modified
214 );
215 }
216 }
217 SweepCommands::Analyze { path, top } => {
218 println!("๐ Analyzing print statement patterns in: {}", path.display());
219 let sweeper = crate::sweeping::src::away::Sweeper::new();
220 let statements = sweeper.scan_directory(&path, true, true)?;
221 if statements.is_empty() {
222 println!(
223 "{}", "โจ Perfectly clean! No print statements found.".green()
224 );
225 return Ok(());
226 }
227 let report = sweeper.analyze_patterns(&statements, top);
228 sweeper.display_report(&report);
229 }
230 SweepCommands::Init { force } => {
231 println!("โ๏ธ Initializing sweep configuration...");
232 let config_path = std::path::PathBuf::from(".sweep.toml");
233 if config_path.exists() && !force {
234 println!(
235 "{} Config file already exists. Use --force to overwrite.", "โ ๏ธ"
236 .yellow()
237 );
238 return Ok(());
239 }
240 crate::sweeping::src::away::create_default_config(&config_path)?;
241 println!(
242 "{} Configuration initialized at: {}", "โ
".green(), config_path
243 .display()
244 );
245 }
246 SweepCommands::Help => {
247 println!("๐งน Sweep - Debug statement cleaner and code formatter");
248 println!("\nCommands:");
249 println!(" scan - Scan for print statements");
250 println!(" sweep - Clean debug statements");
251 println!(" convert - Convert to logging");
252 println!(" analyze - Analyze patterns");
253 println!(" init - Initialize configuration");
254 println!(" help - Show this help message");
255 return Ok(());
256 }
257 }
258 Ok(())
259}
260pub mod src;