11//! Analyze command implementation
22
33use crate :: cli:: { Cli , Commands , OutputFormat } ;
4- use crate :: output:: { OutputFormatter , AnalysisResult } ;
5- use anyhow:: { Result , Context , bail } ;
4+ use crate :: output:: { AnalysisResult , OutputFormatter } ;
5+ use anyhow:: { bail , Context , Result } ;
66use colored:: * ;
77use console:: Term ;
88use indicatif:: { ProgressBar , ProgressStyle } ;
9- use smart_diff_parser:: { tree_sitter:: TreeSitterParser , Parser , LanguageDetector , Language } ;
9+ use smart_diff_parser:: { tree_sitter:: TreeSitterParser , Language , LanguageDetector , Parser } ;
1010use smart_diff_semantic:: { SemanticAnalyzer , SymbolTable } ;
1111use std:: collections:: HashMap ;
1212use std:: path:: { Path , PathBuf } ;
1313use std:: time:: Instant ;
1414use tokio:: fs as async_fs;
15- use tracing:: { info , warn , debug } ;
15+ use tracing:: { debug , info , warn } ;
1616
1717pub async fn run ( cli : Cli ) -> Result < ( ) > {
1818 if let Commands :: Analyze {
@@ -28,7 +28,7 @@ pub async fn run(cli: Cli) -> Result<()> {
2828 {
2929 let start_time = Instant :: now ( ) ;
3030 let term = Term :: stdout ( ) ;
31-
31+
3232 if !cli. quiet {
3333 println ! ( "{}" , "Smart Code Analysis" . bold( ) . blue( ) ) ;
3434 println ! ( "{}" , "=" . repeat( 30 ) . dimmed( ) ) ;
@@ -59,7 +59,8 @@ pub async fn run(cli: Cli) -> Result<()> {
5959 pb. set_position ( 10 ) ;
6060 }
6161
62- let files = discover_analysis_files ( & path, recursive) . await
62+ let files = discover_analysis_files ( & path, recursive)
63+ . await
6364 . context ( "Failed to discover files for analysis" ) ?;
6465
6566 if files. is_empty ( ) {
@@ -82,9 +83,12 @@ pub async fn run(cli: Cli) -> Result<()> {
8283 let total_files = files. len ( ) ;
8384 for ( index, file_path) in files. iter ( ) . enumerate ( ) {
8485 if let Some ( ref pb) = progress {
85- pb. set_message ( format ! ( "Analyzing {}/{}: {}" ,
86- index + 1 , total_files,
87- file_path. file_name( ) . unwrap_or_default( ) . to_string_lossy( ) ) ) ;
86+ pb. set_message ( format ! (
87+ "Analyzing {}/{}: {}" ,
88+ index + 1 ,
89+ total_files,
90+ file_path. file_name( ) . unwrap_or_default( ) . to_string_lossy( )
91+ ) ) ;
8892 pb. set_position ( 20 + ( 60 * index as u64 ) / total_files as u64 ) ;
8993 }
9094
@@ -97,7 +101,8 @@ pub async fn run(cli: Cli) -> Result<()> {
97101 dependencies,
98102 signatures,
99103 & cli,
100- ) . await ;
104+ )
105+ . await ;
101106
102107 match file_result {
103108 Ok ( result) => {
@@ -106,10 +111,12 @@ pub async fn run(cli: Cli) -> Result<()> {
106111 Err ( e) => {
107112 warn ! ( "Failed to analyze file {:?}: {}" , file_path, e) ;
108113 if !cli. quiet {
109- eprintln ! ( "{} Failed to analyze {}: {}" ,
110- "Warning:" . yellow( ) . bold( ) ,
111- file_path. display( ) ,
112- e) ;
114+ eprintln ! (
115+ "{} Failed to analyze {}: {}" ,
116+ "Warning:" . yellow( ) . bold( ) ,
117+ file_path. display( ) ,
118+ e
119+ ) ;
113120 }
114121 }
115122 }
@@ -121,19 +128,17 @@ pub async fn run(cli: Cli) -> Result<()> {
121128 pb. set_position ( 90 ) ;
122129 }
123130
124- let output_content = OutputFormatter :: format_analysis_results (
125- & analysis_results,
126- & format,
127- cli. no_color ,
128- ) ?;
131+ let output_content =
132+ OutputFormatter :: format_analysis_results ( & analysis_results, & format, cli. no_color ) ?;
129133
130134 // Step 5: Write output
131135 if let Some ( ref pb) = progress {
132136 pb. set_message ( "Writing output..." ) ;
133137 pb. set_position ( 95 ) ;
134138 }
135139
136- write_analysis_output ( & output_content, & output, & format) . await
140+ write_analysis_output ( & output_content, & output, & format)
141+ . await
137142 . context ( "Failed to write output" ) ?;
138143
139144 if let Some ( ref pb) = progress {
@@ -195,11 +200,23 @@ async fn collect_source_files(dir: &Path, recursive: bool, files: &mut Vec<PathB
195200fn is_source_file ( path : & Path ) -> bool {
196201 if let Some ( extension) = path. extension ( ) {
197202 let ext = extension. to_string_lossy ( ) . to_lowercase ( ) ;
198- matches ! ( ext. as_str( ) ,
199- "java" | "py" | "pyx" | "pyi" |
200- "js" | "jsx" | "mjs" | "cjs" |
201- "cpp" | "cxx" | "cc" | "hpp" | "hxx" | "h" |
202- "c"
203+ matches ! (
204+ ext. as_str( ) ,
205+ "java"
206+ | "py"
207+ | "pyx"
208+ | "pyi"
209+ | "js"
210+ | "jsx"
211+ | "mjs"
212+ | "cjs"
213+ | "cpp"
214+ | "cxx"
215+ | "cc"
216+ | "hpp"
217+ | "hxx"
218+ | "h"
219+ | "c"
203220 )
204221 } else {
205222 false
@@ -220,12 +237,14 @@ async fn analyze_file(
220237 let file_start = Instant :: now ( ) ;
221238
222239 // Read file content
223- let content = async_fs:: read_to_string ( file_path) . await
240+ let content = async_fs:: read_to_string ( file_path)
241+ . await
224242 . with_context ( || format ! ( "Failed to read file: {}" , file_path. display( ) ) ) ?;
225243
226244 // Detect language
227245 let detected_language = if let Some ( lang_override) = language_override {
228- lang_override. to_parser_language ( )
246+ lang_override
247+ . to_parser_language ( )
229248 . context ( "Invalid language override" ) ?
230249 } else {
231250 let detected = LanguageDetector :: detect_from_path ( file_path) ;
@@ -236,19 +255,26 @@ async fn analyze_file(
236255 }
237256 } ;
238257
239- debug ! ( "Detected language: {:?} for file: {}" , detected_language, file_path. display( ) ) ;
258+ debug ! (
259+ "Detected language: {:?} for file: {}" ,
260+ detected_language,
261+ file_path. display( )
262+ ) ;
240263
241264 // Get or create parser for this language
242- let parser = parsers. entry ( detected_language)
265+ let parser = parsers
266+ . entry ( detected_language)
243267 . or_insert_with ( || TreeSitterParser :: new ( ) . expect ( "Failed to create parser" ) ) ;
244268
245269 // Parse file
246- let ast = parser. parse ( & content, detected_language)
270+ let ast = parser
271+ . parse ( & content, detected_language)
247272 . with_context ( || format ! ( "Failed to parse file: {}" , file_path. display( ) ) ) ?;
248273
249274 // Perform semantic analysis
250275 let mut semantic_analyzer = SemanticAnalyzer :: new ( ) ;
251- let symbols = semantic_analyzer. analyze ( & ast)
276+ let symbols = semantic_analyzer
277+ . analyze ( & ast)
252278 . with_context ( || format ! ( "Failed to analyze file: {}" , file_path. display( ) ) ) ?;
253279
254280 // Complexity analysis - simplified for now
@@ -285,7 +311,11 @@ async fn analyze_file(
285311 } ;
286312
287313 if cli. verbose {
288- info ! ( "Analyzed {} in {:?}" , file_path. display( ) , file_start. elapsed( ) ) ;
314+ info ! (
315+ "Analyzed {} in {:?}" ,
316+ file_path. display( ) ,
317+ file_start. elapsed( )
318+ ) ;
289319 }
290320
291321 Ok ( result)
@@ -315,13 +345,15 @@ async fn write_analysis_output(
315345 match output_path {
316346 Some ( path) => {
317347 if let Some ( parent) = path. parent ( ) {
318- async_fs:: create_dir_all ( parent) . await
319- . with_context ( || format ! ( "Failed to create output directory: {}" , parent. display( ) ) ) ?;
348+ async_fs:: create_dir_all ( parent) . await . with_context ( || {
349+ format ! ( "Failed to create output directory: {}" , parent. display( ) )
350+ } ) ?;
320351 }
321352
322- async_fs:: write ( path, content) . await
353+ async_fs:: write ( path, content)
354+ . await
323355 . with_context ( || format ! ( "Failed to write output to: {}" , path. display( ) ) ) ?;
324-
356+
325357 info ! ( "Analysis output written to: {}" , path. display( ) ) ;
326358 }
327359 None => {
@@ -341,22 +373,40 @@ fn display_analysis_summary(
341373 term. write_line ( "" ) ?;
342374 term. write_line ( & format ! ( "{}" , "Analysis Summary" . bold( ) . green( ) ) ) ?;
343375 term. write_line ( & format ! ( "{}" , "-" . repeat( 20 ) . dimmed( ) ) ) ?;
344-
376+
345377 let total_files = results. len ( ) ;
346378 let total_lines: usize = results. iter ( ) . map ( |r| r. line_count ) . sum ( ) ;
347- let total_functions: usize = results. iter ( ) . map ( |r| {
348- let stats = r. symbols . get_statistics ( ) ;
349- stats. function_count + stats. method_count
350- } ) . sum ( ) ;
351-
352- term. write_line ( & format ! ( "Files analyzed: {}" , total_files. to_string( ) . bold( ) ) ) ?;
379+ let total_functions: usize = results
380+ . iter ( )
381+ . map ( |r| {
382+ let stats = r. symbols . get_statistics ( ) ;
383+ stats. function_count + stats. method_count
384+ } )
385+ . sum ( ) ;
386+
387+ term. write_line ( & format ! (
388+ "Files analyzed: {}" ,
389+ total_files. to_string( ) . bold( )
390+ ) ) ?;
353391 term. write_line ( & format ! ( "Total lines: {}" , total_lines. to_string( ) . bold( ) ) ) ?;
354- term. write_line ( & format ! ( "Total functions: {}" , total_functions. to_string( ) . bold( ) ) ) ?;
355- term. write_line ( & format ! ( "Processing time: {}" , format_duration( elapsed) . bold( ) ) ) ?;
356-
392+ term. write_line ( & format ! (
393+ "Total functions: {}" ,
394+ total_functions. to_string( ) . bold( )
395+ ) ) ?;
396+ term. write_line ( & format ! (
397+ "Processing time: {}" ,
398+ format_duration( elapsed) . bold( )
399+ ) ) ?;
400+
357401 if total_files > 0 {
358- term. write_line ( & format ! ( "Average lines per file: {}" , ( total_lines / total_files) . to_string( ) . bold( ) ) ) ?;
359- term. write_line ( & format ! ( "Files per second: {:.1}" , total_files as f64 / elapsed. as_secs_f64( ) ) ) ?;
402+ term. write_line ( & format ! (
403+ "Average lines per file: {}" ,
404+ ( total_lines / total_files) . to_string( ) . bold( )
405+ ) ) ?;
406+ term. write_line ( & format ! (
407+ "Files per second: {:.1}" ,
408+ total_files as f64 / elapsed. as_secs_f64( )
409+ ) ) ?;
360410 }
361411
362412 Ok ( ( ) )
@@ -365,7 +415,7 @@ fn display_analysis_summary(
365415/// Format duration for display
366416fn format_duration ( duration : std:: time:: Duration ) -> String {
367417 let total_ms = duration. as_millis ( ) ;
368-
418+
369419 if total_ms < 1000 {
370420 format ! ( "{}ms" , total_ms)
371421 } else if total_ms < 60_000 {
0 commit comments