1use crate::analyzer::display::BoxDrawer;
4use crate::analyzer::{
5 ArchitecturePattern, DetectedTechnology, DockerAnalysis, LibraryType, OrchestrationPattern,
6 ProjectCategory, TechnologyCategory,
7};
8use colored::*;
9
10pub fn get_category_emoji(category: &ProjectCategory) -> &'static str {
12 match category {
13 ProjectCategory::Frontend => "π",
14 ProjectCategory::Backend => "βοΈ",
15 ProjectCategory::Api => "π",
16 ProjectCategory::Service => "π",
17 ProjectCategory::Library => "π",
18 ProjectCategory::Tool => "π§",
19 ProjectCategory::Documentation => "π",
20 ProjectCategory::Infrastructure => "ποΈ",
21 ProjectCategory::Unknown => "β",
22 }
23}
24
25pub fn format_project_category(category: &ProjectCategory) -> &'static str {
27 match category {
28 ProjectCategory::Frontend => "Frontend",
29 ProjectCategory::Backend => "Backend",
30 ProjectCategory::Api => "API",
31 ProjectCategory::Service => "Service",
32 ProjectCategory::Library => "Library",
33 ProjectCategory::Tool => "Tool",
34 ProjectCategory::Documentation => "Documentation",
35 ProjectCategory::Infrastructure => "Infrastructure",
36 ProjectCategory::Unknown => "Unknown",
37 }
38}
39
40pub fn display_architecture_description(pattern: &ArchitecturePattern) {
42 match pattern {
43 ArchitecturePattern::Monolithic => {
44 println!(" π¦ This is a single, self-contained application");
45 }
46 ArchitecturePattern::Fullstack => {
47 println!(" π This is a full-stack application with separate frontend and backend");
48 }
49 ArchitecturePattern::Microservices => {
50 println!(
51 " π This is a microservices architecture with multiple independent services"
52 );
53 }
54 ArchitecturePattern::ApiFirst => {
55 println!(" π This is an API-first architecture focused on service interfaces");
56 }
57 ArchitecturePattern::EventDriven => {
58 println!(" π‘ This is an event-driven architecture with decoupled components");
59 }
60 ArchitecturePattern::Mixed => {
61 println!(" π This is a mixed architecture combining multiple patterns");
62 }
63 }
64}
65
66pub fn display_architecture_description_to_string(pattern: &ArchitecturePattern) -> String {
68 match pattern {
69 ArchitecturePattern::Monolithic => {
70 " π¦ This is a single, self-contained application\n".to_string()
71 }
72 ArchitecturePattern::Fullstack => {
73 " π This is a full-stack application with separate frontend and backend\n"
74 .to_string()
75 }
76 ArchitecturePattern::Microservices => {
77 " π This is a microservices architecture with multiple independent services\n"
78 .to_string()
79 }
80 ArchitecturePattern::ApiFirst => {
81 " π This is an API-first architecture focused on service interfaces\n".to_string()
82 }
83 ArchitecturePattern::EventDriven => {
84 " π‘ This is an event-driven architecture with decoupled components\n".to_string()
85 }
86 ArchitecturePattern::Mixed => {
87 " π This is a mixed architecture combining multiple patterns\n".to_string()
88 }
89 }
90}
91
92pub fn get_main_technologies(technologies: &[DetectedTechnology]) -> String {
94 let primary = technologies.iter().find(|t| t.is_primary);
95 let frameworks: Vec<_> = technologies
96 .iter()
97 .filter(|t| {
98 matches!(
99 t.category,
100 TechnologyCategory::FrontendFramework | TechnologyCategory::MetaFramework
101 )
102 })
103 .take(2)
104 .collect();
105
106 let mut result = Vec::new();
107
108 if let Some(p) = primary {
109 result.push(p.name.clone());
110 }
111
112 for f in frameworks {
113 if Some(&f.name) != primary.map(|p| &p.name) {
114 result.push(f.name.clone());
115 }
116 }
117
118 if result.is_empty() {
119 "-".to_string()
120 } else {
121 result.join(", ")
122 }
123}
124
125pub fn add_confidence_bar_to_drawer(score: f32, box_drawer: &mut BoxDrawer) {
127 let percentage = (score * 100.0) as u8;
128 let bar_width = 20;
129 let filled = ((score * bar_width as f32) as usize).min(bar_width);
130
131 let bar = format!(
132 "{}{}",
133 "β".repeat(filled).green(),
134 "β".repeat(bar_width - filled).dimmed()
135 );
136
137 let color = if percentage >= 80 {
138 "green"
139 } else if percentage >= 60 {
140 "yellow"
141 } else {
142 "red"
143 };
144
145 let confidence_info = format!("{} {}", bar, format!("{:.0}%", percentage).color(color));
146 box_drawer.add_line("Confidence:", &confidence_info, true);
147}
148
149pub fn display_technologies_detailed_legacy(technologies: &[DetectedTechnology]) {
151 let mut by_category: std::collections::HashMap<&TechnologyCategory, Vec<&DetectedTechnology>> =
153 std::collections::HashMap::new();
154
155 for tech in technologies {
156 by_category.entry(&tech.category).or_default().push(tech);
157 }
158
159 if let Some(primary) = technologies.iter().find(|t| t.is_primary) {
161 println!("\nπ οΈ Technology Stack:");
162 println!(
163 " π― PRIMARY: {} (confidence: {:.1}%)",
164 primary.name,
165 primary.confidence * 100.0
166 );
167 println!(" Architecture driver for this project");
168 }
169
170 let categories = [
172 (TechnologyCategory::MetaFramework, "ποΈ Meta-Frameworks"),
173 (
174 TechnologyCategory::BackendFramework,
175 "π₯οΈ Backend Frameworks",
176 ),
177 (
178 TechnologyCategory::FrontendFramework,
179 "π¨ Frontend Frameworks",
180 ),
181 (
182 TechnologyCategory::Library(LibraryType::UI),
183 "π¨ UI Libraries",
184 ),
185 (
186 TechnologyCategory::Library(LibraryType::Utility),
187 "π Core Libraries",
188 ),
189 (TechnologyCategory::BuildTool, "π¨ Build Tools"),
190 (TechnologyCategory::PackageManager, "π¦ Package Managers"),
191 (TechnologyCategory::Database, "ποΈ Database & ORM"),
192 (TechnologyCategory::Runtime, "β‘ Runtimes"),
193 (TechnologyCategory::Testing, "π§ͺ Testing"),
194 ];
195
196 for (category, label) in &categories {
197 if let Some(techs) = by_category.get(category)
198 && !techs.is_empty()
199 {
200 println!("\n {}:", label);
201 for tech in techs {
202 println!(
203 " β’ {} (confidence: {:.1}%)",
204 tech.name,
205 tech.confidence * 100.0
206 );
207 if let Some(version) = &tech.version {
208 println!(" Version: {}", version);
209 }
210 }
211 }
212 }
213
214 for (cat, techs) in &by_category {
216 if let TechnologyCategory::Library(lib_type) = cat {
217 let label = match lib_type {
218 LibraryType::StateManagement => "π State Management",
219 LibraryType::DataFetching => "π Data Fetching",
220 LibraryType::Routing => "πΊοΈ Routing",
221 LibraryType::Styling => "π¨ Styling",
222 LibraryType::HttpClient => "π HTTP Clients",
223 LibraryType::Authentication => "π Authentication",
224 LibraryType::Other(_) => "π¦ Other Libraries",
225 _ => continue, };
227
228 if !matches!(lib_type, LibraryType::UI | LibraryType::Utility) && !techs.is_empty() {
230 println!("\n {}:", label);
231 for tech in techs {
232 println!(
233 " β’ {} (confidence: {:.1}%)",
234 tech.name,
235 tech.confidence * 100.0
236 );
237 if let Some(version) = &tech.version {
238 println!(" Version: {}", version);
239 }
240 }
241 }
242 }
243 }
244}
245
246pub fn display_technologies_detailed_legacy_to_string(
248 technologies: &[DetectedTechnology],
249) -> String {
250 let mut output = String::new();
251
252 let mut by_category: std::collections::HashMap<&TechnologyCategory, Vec<&DetectedTechnology>> =
254 std::collections::HashMap::new();
255
256 for tech in technologies {
257 by_category.entry(&tech.category).or_default().push(tech);
258 }
259
260 if let Some(primary) = technologies.iter().find(|t| t.is_primary) {
262 output.push_str("\nπ οΈ Technology Stack:\n");
263 output.push_str(&format!(
264 " π― PRIMARY: {} (confidence: {:.1}%)\n",
265 primary.name,
266 primary.confidence * 100.0
267 ));
268 output.push_str(" Architecture driver for this project\n");
269 }
270
271 let categories = [
273 (TechnologyCategory::MetaFramework, "ποΈ Meta-Frameworks"),
274 (
275 TechnologyCategory::BackendFramework,
276 "π₯οΈ Backend Frameworks",
277 ),
278 (
279 TechnologyCategory::FrontendFramework,
280 "π¨ Frontend Frameworks",
281 ),
282 (
283 TechnologyCategory::Library(LibraryType::UI),
284 "π¨ UI Libraries",
285 ),
286 (
287 TechnologyCategory::Library(LibraryType::Utility),
288 "π Core Libraries",
289 ),
290 (TechnologyCategory::BuildTool, "π¨ Build Tools"),
291 (TechnologyCategory::PackageManager, "π¦ Package Managers"),
292 (TechnologyCategory::Database, "ποΈ Database & ORM"),
293 (TechnologyCategory::Runtime, "β‘ Runtimes"),
294 (TechnologyCategory::Testing, "π§ͺ Testing"),
295 ];
296
297 for (category, label) in &categories {
298 if let Some(techs) = by_category.get(category)
299 && !techs.is_empty()
300 {
301 output.push_str(&format!("\n {}:\n", label));
302 for tech in techs {
303 output.push_str(&format!(
304 " β’ {} (confidence: {:.1}%)\n",
305 tech.name,
306 tech.confidence * 100.0
307 ));
308 if let Some(version) = &tech.version {
309 output.push_str(&format!(" Version: {}\n", version));
310 }
311 }
312 }
313 }
314
315 for (cat, techs) in &by_category {
317 if let TechnologyCategory::Library(lib_type) = cat {
318 let label = match lib_type {
319 LibraryType::StateManagement => "π State Management",
320 LibraryType::DataFetching => "π Data Fetching",
321 LibraryType::Routing => "πΊοΈ Routing",
322 LibraryType::Styling => "π¨ Styling",
323 LibraryType::HttpClient => "π HTTP Clients",
324 LibraryType::Authentication => "π Authentication",
325 LibraryType::Other(_) => "π¦ Other Libraries",
326 _ => continue, };
328
329 if !matches!(lib_type, LibraryType::UI | LibraryType::Utility) && !techs.is_empty() {
331 output.push_str(&format!("\n {}:\n", label));
332 for tech in techs {
333 output.push_str(&format!(
334 " β’ {} (confidence: {:.1}%)\n",
335 tech.name,
336 tech.confidence * 100.0
337 ));
338 if let Some(version) = &tech.version {
339 output.push_str(&format!(" Version: {}\n", version));
340 }
341 }
342 }
343 }
344 }
345
346 output
347}
348
349pub fn display_docker_analysis_detailed_legacy(docker_analysis: &DockerAnalysis) {
351 println!("\n π³ Docker Infrastructure Analysis:");
352
353 if !docker_analysis.dockerfiles.is_empty() {
355 println!(
356 " π Dockerfiles ({}):",
357 docker_analysis.dockerfiles.len()
358 );
359 for dockerfile in &docker_analysis.dockerfiles {
360 println!(" β’ {}", dockerfile.path.display());
361 if let Some(env) = &dockerfile.environment {
362 println!(" Environment: {}", env);
363 }
364 if let Some(base_image) = &dockerfile.base_image {
365 println!(" Base image: {}", base_image);
366 }
367 if !dockerfile.exposed_ports.is_empty() {
368 println!(
369 " Exposed ports: {}",
370 dockerfile
371 .exposed_ports
372 .iter()
373 .map(|p| p.to_string())
374 .collect::<Vec<_>>()
375 .join(", ")
376 );
377 }
378 if dockerfile.is_multistage {
379 println!(
380 " Multi-stage build: {} stages",
381 dockerfile.build_stages.len()
382 );
383 }
384 println!(" Instructions: {}", dockerfile.instruction_count);
385 }
386 }
387
388 if !docker_analysis.compose_files.is_empty() {
390 println!(
391 " π Compose Files ({}):",
392 docker_analysis.compose_files.len()
393 );
394 for compose_file in &docker_analysis.compose_files {
395 println!(" β’ {}", compose_file.path.display());
396 if let Some(env) = &compose_file.environment {
397 println!(" Environment: {}", env);
398 }
399 if let Some(version) = &compose_file.version {
400 println!(" Version: {}", version);
401 }
402 if !compose_file.service_names.is_empty() {
403 println!(
404 " Services: {}",
405 compose_file.service_names.join(", ")
406 );
407 }
408 if !compose_file.networks.is_empty() {
409 println!(" Networks: {}", compose_file.networks.join(", "));
410 }
411 if !compose_file.volumes.is_empty() {
412 println!(" Volumes: {}", compose_file.volumes.join(", "));
413 }
414 }
415 }
416
417 println!(
419 " ποΈ Orchestration Pattern: {:?}",
420 docker_analysis.orchestration_pattern
421 );
422 match docker_analysis.orchestration_pattern {
423 OrchestrationPattern::SingleContainer => {
424 println!(" Simple containerized application");
425 }
426 OrchestrationPattern::DockerCompose => {
427 println!(" Multi-service Docker Compose setup");
428 }
429 OrchestrationPattern::Microservices => {
430 println!(" Microservices architecture with service discovery");
431 }
432 OrchestrationPattern::EventDriven => {
433 println!(" Event-driven architecture with message queues");
434 }
435 OrchestrationPattern::ServiceMesh => {
436 println!(" Service mesh for advanced service communication");
437 }
438 OrchestrationPattern::Mixed => {
439 println!(" Mixed/complex orchestration pattern");
440 }
441 }
442}
443
444pub fn display_docker_analysis_detailed_legacy_to_string(
446 docker_analysis: &DockerAnalysis,
447) -> String {
448 let mut output = String::new();
449
450 output.push_str("\n π³ Docker Infrastructure Analysis:\n");
451
452 if !docker_analysis.dockerfiles.is_empty() {
454 output.push_str(&format!(
455 " π Dockerfiles ({}):\n",
456 docker_analysis.dockerfiles.len()
457 ));
458 for dockerfile in &docker_analysis.dockerfiles {
459 output.push_str(&format!(" β’ {}\n", dockerfile.path.display()));
460 if let Some(env) = &dockerfile.environment {
461 output.push_str(&format!(" Environment: {}\n", env));
462 }
463 if let Some(base_image) = &dockerfile.base_image {
464 output.push_str(&format!(" Base image: {}\n", base_image));
465 }
466 if !dockerfile.exposed_ports.is_empty() {
467 output.push_str(&format!(
468 " Exposed ports: {}\n",
469 dockerfile
470 .exposed_ports
471 .iter()
472 .map(|p| p.to_string())
473 .collect::<Vec<_>>()
474 .join(", ")
475 ));
476 }
477 if dockerfile.is_multistage {
478 output.push_str(&format!(
479 " Multi-stage build: {} stages\n",
480 dockerfile.build_stages.len()
481 ));
482 }
483 output.push_str(&format!(
484 " Instructions: {}\n",
485 dockerfile.instruction_count
486 ));
487 }
488 }
489
490 if !docker_analysis.compose_files.is_empty() {
492 output.push_str(&format!(
493 " π Compose Files ({}):\n",
494 docker_analysis.compose_files.len()
495 ));
496 for compose_file in &docker_analysis.compose_files {
497 output.push_str(&format!(" β’ {}\n", compose_file.path.display()));
498 if let Some(env) = &compose_file.environment {
499 output.push_str(&format!(" Environment: {}\n", env));
500 }
501 if let Some(version) = &compose_file.version {
502 output.push_str(&format!(" Version: {}\n", version));
503 }
504 if !compose_file.service_names.is_empty() {
505 output.push_str(&format!(
506 " Services: {}\n",
507 compose_file.service_names.join(", ")
508 ));
509 }
510 if !compose_file.networks.is_empty() {
511 output.push_str(&format!(
512 " Networks: {}\n",
513 compose_file.networks.join(", ")
514 ));
515 }
516 if !compose_file.volumes.is_empty() {
517 output.push_str(&format!(
518 " Volumes: {}\n",
519 compose_file.volumes.join(", ")
520 ));
521 }
522 }
523 }
524
525 output.push_str(&format!(
527 " ποΈ Orchestration Pattern: {:?}\n",
528 docker_analysis.orchestration_pattern
529 ));
530 match docker_analysis.orchestration_pattern {
531 OrchestrationPattern::SingleContainer => {
532 output.push_str(" Simple containerized application\n");
533 }
534 OrchestrationPattern::DockerCompose => {
535 output.push_str(" Multi-service Docker Compose setup\n");
536 }
537 OrchestrationPattern::Microservices => {
538 output.push_str(" Microservices architecture with service discovery\n");
539 }
540 OrchestrationPattern::EventDriven => {
541 output.push_str(" Event-driven architecture with message queues\n");
542 }
543 OrchestrationPattern::ServiceMesh => {
544 output.push_str(" Service mesh for advanced service communication\n");
545 }
546 OrchestrationPattern::Mixed => {
547 output.push_str(" Mixed/complex orchestration pattern\n");
548 }
549 }
550
551 output
552}