@@ -13,11 +13,14 @@ pub async fn handle_dependencies(
1313 _prod_only : bool ,
1414 _dev_only : bool ,
1515 format : OutputFormat ,
16- ) -> crate :: Result < ( ) > {
16+ ) -> crate :: Result < String > {
1717 let project_path = path. canonicalize ( )
1818 . unwrap_or_else ( |_| path. clone ( ) ) ;
1919
20- println ! ( "🔍 Analyzing dependencies: {}" , project_path. display( ) ) ;
20+ let mut output = String :: new ( ) ;
21+ let header = format ! ( "🔍 Analyzing dependencies: {}\n " , project_path. display( ) ) ;
22+ println ! ( "{}" , header) ;
23+ output. push_str ( & header) ;
2124
2225 // First, analyze the project using monorepo analysis
2326 let monorepo_analysis = analyze_monorepo ( & project_path) ?;
@@ -36,17 +39,20 @@ pub async fn handle_dependencies(
3639 ) . await ?;
3740
3841 if format == OutputFormat :: Table {
39- display_dependencies_table ( & dep_analysis, & monorepo_analysis, licenses, vulnerabilities, & all_languages, & project_path) . await ?;
42+ let table_output = display_dependencies_table ( & dep_analysis, & monorepo_analysis, licenses, vulnerabilities, & all_languages, & project_path) . await ?;
43+ output. push_str ( & table_output) ;
4044 } else if format == OutputFormat :: Json {
4145 // JSON output
42- let output = serde_json:: json!( {
46+ let json_data = serde_json:: json!( {
4347 "dependencies" : dep_analysis. dependencies,
4448 "total" : dep_analysis. dependencies. len( ) ,
4549 } ) ;
46- println ! ( "{}" , serde_json:: to_string_pretty( & output) ?) ;
50+ let json_output = serde_json:: to_string_pretty ( & json_data) ?;
51+ println ! ( "{}" , json_output) ;
52+ output. push_str ( & json_output) ;
4753 }
4854
49- Ok ( ( ) )
55+ Ok ( output )
5056}
5157
5258async fn display_dependencies_table (
@@ -56,58 +62,74 @@ async fn display_dependencies_table(
5662 vulnerabilities : bool ,
5763 all_languages : & [ analyzer:: DetectedLanguage ] ,
5864 project_path : & std:: path:: Path ,
59- ) -> crate :: Result < ( ) > {
65+ ) -> crate :: Result < String > {
6066 use termcolor:: { ColorChoice , StandardStream , WriteColor , ColorSpec , Color } ;
6167
68+ let mut output = String :: new ( ) ;
6269 let mut stdout = StandardStream :: stdout ( ColorChoice :: Always ) ;
6370
6471 // Print summary
65- println ! ( "\n 📦 Dependency Analysis Report" ) ;
66- println ! ( "{}" , "=" . repeat( 80 ) ) ;
72+ let summary_header = format ! ( "\n 📦 Dependency Analysis Report\n {}\n " , "=" . repeat( 80 ) ) ;
73+ println ! ( "{}" , summary_header) ;
74+ output. push_str ( & summary_header) ;
6775
68- let total_deps: usize = dep_analysis. dependencies . len ( ) ;
69- println ! ( "Total dependencies: {}" , total_deps) ;
76+ let total_deps_line = format ! ( "Total dependencies: {}\n " , dep_analysis. dependencies. len( ) ) ;
77+ println ! ( "{}" , total_deps_line) ;
78+ output. push_str ( & total_deps_line) ;
7079
7180 if monorepo_analysis. is_monorepo {
72- println ! ( "Projects analyzed: {}" , monorepo_analysis. projects. len( ) ) ;
81+ let projects_line = format ! ( "Projects analyzed: {}\n " , monorepo_analysis. projects. len( ) ) ;
82+ println ! ( "{}" , projects_line) ;
83+ output. push_str ( & projects_line) ;
7384 for project in & monorepo_analysis. projects {
74- println ! ( " • {} ({})" , project. name, format_project_category( & project. project_category) ) ;
85+ let project_line = format ! ( " • {} ({})\n " , project. name, format_project_category( & project. project_category) ) ;
86+ println ! ( "{}" , project_line) ;
87+ output. push_str ( & project_line) ;
7588 }
7689 }
7790
7891 for ( name, info) in & dep_analysis. dependencies {
79- print ! ( " {} v{}" , name, info. version) ;
92+ let dep_line = format ! ( " {} v{}" , name, info. version) ;
93+ print ! ( "{}" , dep_line) ;
94+ output. push_str ( & dep_line) ;
8095
8196 // Color code by type
8297 stdout. set_color ( ColorSpec :: new ( ) . set_fg ( Some (
8398 if info. is_dev { Color :: Yellow } else { Color :: Green }
8499 ) ) ) ?;
85100
86- print ! ( " [{}]" , if info. is_dev { "dev" } else { "prod" } ) ;
101+ let type_tag = format ! ( " [{}]" , if info. is_dev { "dev" } else { "prod" } ) ;
102+ print ! ( "{}" , type_tag) ;
103+ output. push_str ( & type_tag) ;
87104
88105 stdout. reset ( ) ?;
89106
90107 if licenses && info. license . is_some ( ) {
91- print ! ( " - License: {}" , info. license. as_ref( ) . unwrap_or( & "Unknown" . to_string( ) ) ) ;
108+ let license_info = format ! ( " - License: {}" , info. license. as_ref( ) . unwrap_or( & "Unknown" . to_string( ) ) ) ;
109+ print ! ( "{}" , license_info) ;
110+ output. push_str ( & license_info) ;
92111 }
93112
94113 println ! ( ) ;
114+ output. push ( '\n' ) ;
95115 }
96116
97117 if licenses {
98- display_license_summary ( & dep_analysis. dependencies ) ;
118+ let license_output = display_license_summary ( & dep_analysis. dependencies ) ;
119+ output. push_str ( & license_output) ;
99120 }
100121
101122 if vulnerabilities {
102- check_and_display_vulnerabilities ( dep_analysis, all_languages, project_path) . await ?;
123+ let vuln_output = check_and_display_vulnerabilities ( dep_analysis, all_languages, project_path) . await ?;
124+ output. push_str ( & vuln_output) ;
103125 }
104126
105- Ok ( ( ) )
127+ Ok ( output )
106128}
107129
108- fn display_license_summary ( dependencies : & analyzer:: dependency_parser:: DetailedDependencyMap ) {
109- println ! ( " \n 📋 License Summary" ) ;
110- println ! ( "{} " , "-" . repeat( 80 ) ) ;
130+ fn display_license_summary ( dependencies : & analyzer:: dependency_parser:: DetailedDependencyMap ) -> String {
131+ let mut output = String :: new ( ) ;
132+ output . push_str ( & format ! ( "\n 📋 License Summary \n {} \n " , "-" . repeat( 80 ) ) ) ;
111133
112134 let mut license_counts: HashMap < String , usize > = HashMap :: new ( ) ;
113135
@@ -121,18 +143,24 @@ fn display_license_summary(dependencies: &analyzer::dependency_parser::DetailedD
121143 licenses. sort_by ( |a, b| b. 1 . cmp ( & a. 1 ) ) ;
122144
123145 for ( license, count) in licenses {
124- println ! ( " {}: {} packages" , license, count) ;
146+ output . push_str ( & format ! ( " {}: {} packages\n " , license, count) ) ;
125147 }
148+
149+ println ! ( "{}" , output) ;
150+ output
126151}
127152
128153async fn check_and_display_vulnerabilities (
129154 dep_analysis : & analyzer:: dependency_parser:: DependencyAnalysis ,
130155 all_languages : & [ analyzer:: DetectedLanguage ] ,
131156 project_path : & std:: path:: Path ,
132- ) -> crate :: Result < ( ) > {
157+ ) -> crate :: Result < String > {
133158 use termcolor:: { ColorChoice , StandardStream , WriteColor , ColorSpec , Color } ;
134159
160+ let mut output = String :: new ( ) ;
161+
135162 println ! ( "\n 🔍 Checking for vulnerabilities..." ) ;
163+ output. push_str ( "\n 🔍 Checking for vulnerabilities...\n " ) ;
136164
137165 // Convert DetailedDependencyMap to the format expected by VulnerabilityChecker
138166 let mut deps_by_language: HashMap < analyzer:: dependency_parser:: Language , Vec < analyzer:: dependency_parser:: DependencyInfo > > = HashMap :: new ( ) ;
@@ -199,17 +227,25 @@ async fn check_and_display_vulnerabilities(
199227 Ok ( report) => {
200228 let mut stdout = StandardStream :: stdout ( ColorChoice :: Always ) ;
201229
202- println ! ( "\n 🛡️ Vulnerability Report" ) ;
203- println ! ( "{}" , "-" . repeat( 80 ) ) ;
204- println ! ( "Checked at: {}" , report. checked_at. format( "%Y-%m-%d %H:%M:%S UTC" ) ) ;
205- println ! ( "Total vulnerabilities: {}" , report. total_vulnerabilities) ;
230+ let report_header = format ! ( "\n 🛡️ Vulnerability Report\n {}\n Checked at: {}\n Total vulnerabilities: {}\n " ,
231+ "-" . repeat( 80 ) ,
232+ report. checked_at. format( "%Y-%m-%d %H:%M:%S UTC" ) ,
233+ report. total_vulnerabilities
234+ ) ;
235+ println ! ( "{}" , report_header) ;
236+ output. push_str ( & report_header) ;
206237
207238 if report. total_vulnerabilities > 0 {
208- display_vulnerability_breakdown ( & report, & mut stdout) ?;
209- display_vulnerable_dependencies ( & report, & mut stdout) ?;
239+ let breakdown_output = display_vulnerability_breakdown ( & report, & mut stdout) ?;
240+ output. push_str ( & breakdown_output) ;
241+
242+ let deps_output = display_vulnerable_dependencies ( & report, & mut stdout) ?;
243+ output. push_str ( & deps_output) ;
210244 } else {
211245 stdout. set_color ( ColorSpec :: new ( ) . set_fg ( Some ( Color :: Green ) ) ) ?;
212- println ! ( "\n ✅ No known vulnerabilities found!" ) ;
246+ let no_vulns_message = "\n ✅ No known vulnerabilities found!\n " ;
247+ println ! ( "{}" , no_vulns_message) ;
248+ output. push_str ( no_vulns_message) ;
213249 stdout. reset ( ) ?;
214250 }
215251 }
@@ -219,56 +255,72 @@ async fn check_and_display_vulnerabilities(
219255 }
220256 }
221257
222- Ok ( ( ) )
258+ Ok ( output )
223259}
224260
225261fn display_vulnerability_breakdown (
226262 report : & analyzer:: vulnerability_checker:: VulnerabilityReport ,
227263 stdout : & mut termcolor:: StandardStream ,
228- ) -> crate :: Result < ( ) > {
264+ ) -> crate :: Result < String > {
229265 use termcolor:: { WriteColor , ColorSpec , Color } ;
230266
231- println ! ( "\n Severity Breakdown:" ) ;
267+ let mut output = String :: new ( ) ;
268+
269+ output. push_str ( "\n Severity Breakdown:\n " ) ;
232270 if report. critical_count > 0 {
233271 stdout. set_color ( ColorSpec :: new ( ) . set_fg ( Some ( Color :: Red ) ) . set_bold ( true ) ) ?;
234- println ! ( " CRITICAL: {}" , report. critical_count) ;
272+ let critical_line = format ! ( " CRITICAL: {}\n " , report. critical_count) ;
273+ output. push_str ( & critical_line) ;
274+ print ! ( "{}" , critical_line) ;
235275 stdout. reset ( ) ?;
236276 }
237277 if report. high_count > 0 {
238278 stdout. set_color ( ColorSpec :: new ( ) . set_fg ( Some ( Color :: Red ) ) ) ?;
239- println ! ( " HIGH: {}" , report. high_count) ;
279+ let high_line = format ! ( " HIGH: {}\n " , report. high_count) ;
280+ output. push_str ( & high_line) ;
281+ print ! ( "{}" , high_line) ;
240282 stdout. reset ( ) ?;
241283 }
242284 if report. medium_count > 0 {
243285 stdout. set_color ( ColorSpec :: new ( ) . set_fg ( Some ( Color :: Yellow ) ) ) ?;
244- println ! ( " MEDIUM: {}" , report. medium_count) ;
286+ let medium_line = format ! ( " MEDIUM: {}\n " , report. medium_count) ;
287+ output. push_str ( & medium_line) ;
288+ print ! ( "{}" , medium_line) ;
245289 stdout. reset ( ) ?;
246290 }
247291 if report. low_count > 0 {
248292 stdout. set_color ( ColorSpec :: new ( ) . set_fg ( Some ( Color :: Blue ) ) ) ?;
249- println ! ( " LOW: {}" , report. low_count) ;
293+ let low_line = format ! ( " LOW: {}\n " , report. low_count) ;
294+ output. push_str ( & low_line) ;
295+ print ! ( "{}" , low_line) ;
250296 stdout. reset ( ) ?;
251297 }
252298
253- Ok ( ( ) )
299+ Ok ( output )
254300}
255301
256302fn display_vulnerable_dependencies (
257303 report : & analyzer:: vulnerability_checker:: VulnerabilityReport ,
258304 stdout : & mut termcolor:: StandardStream ,
259- ) -> crate :: Result < ( ) > {
305+ ) -> crate :: Result < String > {
260306 use termcolor:: { WriteColor , ColorSpec , Color } ;
261307
262- println ! ( "\n Vulnerable Dependencies:" ) ;
308+ let mut output = String :: new ( ) ;
309+
310+ output. push_str ( "\n Vulnerable Dependencies:\n " ) ;
263311 for vuln_dep in & report. vulnerable_dependencies {
264- println ! ( "\n 📦 {} v{} ({})" ,
312+ let dep_line = format ! ( "\n 📦 {} v{} ({})\n " ,
265313 vuln_dep. name,
266314 vuln_dep. version,
267315 vuln_dep. language. as_str( )
268316 ) ;
317+ output. push_str ( & dep_line) ;
318+ print ! ( "{}" , dep_line) ;
269319
270320 for vuln in & vuln_dep. vulnerabilities {
271- print ! ( " ⚠️ {} " , vuln. id) ;
321+ let vuln_id_line = format ! ( " ⚠️ {} " , vuln. id) ;
322+ output. push_str ( & vuln_id_line) ;
323+ print ! ( "{}" , vuln_id_line) ;
272324
273325 // Color by severity
274326 stdout. set_color ( ColorSpec :: new ( ) . set_fg ( Some (
@@ -281,28 +333,36 @@ fn display_vulnerable_dependencies(
281333 }
282334 ) ) . set_bold ( vuln. severity == VulnerabilitySeverity :: Critical ) ) ?;
283335
284- print ! ( "[{}]" , match vuln. severity {
285- VulnerabilitySeverity :: Critical => "CRITICAL" ,
286- VulnerabilitySeverity :: High => "HIGH" ,
287- VulnerabilitySeverity :: Medium => "MEDIUM" ,
288- VulnerabilitySeverity :: Low => "LOW" ,
289- VulnerabilitySeverity :: Info => "INFO" ,
290- } ) ;
336+ let severity_tag = match vuln. severity {
337+ VulnerabilitySeverity :: Critical => "[CRITICAL]" ,
338+ VulnerabilitySeverity :: High => "[HIGH]" ,
339+ VulnerabilitySeverity :: Medium => "[MEDIUM]" ,
340+ VulnerabilitySeverity :: Low => "[LOW]" ,
341+ VulnerabilitySeverity :: Info => "[INFO]" ,
342+ } ;
343+ output. push_str ( severity_tag) ;
344+ print ! ( "{}" , severity_tag) ;
291345
292346 stdout. reset ( ) ?;
293347
294- println ! ( " - {}" , vuln. title) ;
348+ let title_line = format ! ( " - {}\n " , vuln. title) ;
349+ output. push_str ( & title_line) ;
350+ print ! ( "{}" , title_line) ;
295351
296352 if let Some ( ref cve) = vuln. cve {
297- println ! ( " CVE: {}" , cve) ;
353+ let cve_line = format ! ( " CVE: {}\n " , cve) ;
354+ output. push_str ( & cve_line) ;
355+ println ! ( "{}" , cve_line. trim_end( ) ) ;
298356 }
299357 if let Some ( ref patched) = vuln. patched_versions {
300358 stdout. set_color ( ColorSpec :: new ( ) . set_fg ( Some ( Color :: Green ) ) ) ?;
301- println ! ( " Fix: Upgrade to {}" , patched) ;
359+ let fix_line = format ! ( " Fix: Upgrade to {}\n " , patched) ;
360+ output. push_str ( & fix_line) ;
361+ println ! ( "{}" , fix_line. trim_end( ) ) ;
302362 stdout. reset ( ) ?;
303363 }
304364 }
305365 }
306366
307- Ok ( ( ) )
367+ Ok ( output )
308368}
0 commit comments