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
157 .entry(&tech.category)
158 .or_insert_with(Vec::new)
159 .push(tech);
160 }
161
162 if let Some(primary) = technologies.iter().find(|t| t.is_primary) {
164 println!("\nπ οΈ Technology Stack:");
165 println!(
166 " π― PRIMARY: {} (confidence: {:.1}%)",
167 primary.name,
168 primary.confidence * 100.0
169 );
170 println!(" Architecture driver for this project");
171 }
172
173 let categories = [
175 (TechnologyCategory::MetaFramework, "ποΈ Meta-Frameworks"),
176 (
177 TechnologyCategory::BackendFramework,
178 "π₯οΈ Backend Frameworks",
179 ),
180 (
181 TechnologyCategory::FrontendFramework,
182 "π¨ Frontend Frameworks",
183 ),
184 (
185 TechnologyCategory::Library(LibraryType::UI),
186 "π¨ UI Libraries",
187 ),
188 (
189 TechnologyCategory::Library(LibraryType::Utility),
190 "π Core Libraries",
191 ),
192 (TechnologyCategory::BuildTool, "π¨ Build Tools"),
193 (TechnologyCategory::PackageManager, "π¦ Package Managers"),
194 (TechnologyCategory::Database, "ποΈ Database & ORM"),
195 (TechnologyCategory::Runtime, "β‘ Runtimes"),
196 (TechnologyCategory::Testing, "π§ͺ Testing"),
197 ];
198
199 for (category, label) in &categories {
200 if let Some(techs) = by_category.get(category) {
201 if !techs.is_empty() {
202 println!("\n {}:", label);
203 for tech in techs {
204 println!(
205 " β’ {} (confidence: {:.1}%)",
206 tech.name,
207 tech.confidence * 100.0
208 );
209 if let Some(version) = &tech.version {
210 println!(" Version: {}", version);
211 }
212 }
213 }
214 }
215 }
216
217 for (cat, techs) in &by_category {
219 match cat {
220 TechnologyCategory::Library(lib_type) => {
221 let label = match lib_type {
222 LibraryType::StateManagement => "π State Management",
223 LibraryType::DataFetching => "π Data Fetching",
224 LibraryType::Routing => "πΊοΈ Routing",
225 LibraryType::Styling => "π¨ Styling",
226 LibraryType::HttpClient => "π HTTP Clients",
227 LibraryType::Authentication => "π Authentication",
228 LibraryType::Other(_) => "π¦ Other Libraries",
229 _ => continue, };
231
232 if !matches!(lib_type, LibraryType::UI | LibraryType::Utility) && !techs.is_empty()
234 {
235 println!("\n {}:", label);
236 for tech in techs {
237 println!(
238 " β’ {} (confidence: {:.1}%)",
239 tech.name,
240 tech.confidence * 100.0
241 );
242 if let Some(version) = &tech.version {
243 println!(" Version: {}", version);
244 }
245 }
246 }
247 }
248 _ => {} }
250 }
251}
252
253pub fn display_technologies_detailed_legacy_to_string(
255 technologies: &[DetectedTechnology],
256) -> String {
257 let mut output = String::new();
258
259 let mut by_category: std::collections::HashMap<&TechnologyCategory, Vec<&DetectedTechnology>> =
261 std::collections::HashMap::new();
262
263 for tech in technologies {
264 by_category
265 .entry(&tech.category)
266 .or_insert_with(Vec::new)
267 .push(tech);
268 }
269
270 if let Some(primary) = technologies.iter().find(|t| t.is_primary) {
272 output.push_str("\nπ οΈ Technology Stack:\n");
273 output.push_str(&format!(
274 " π― PRIMARY: {} (confidence: {:.1}%)\n",
275 primary.name,
276 primary.confidence * 100.0
277 ));
278 output.push_str(" Architecture driver for this project\n");
279 }
280
281 let categories = [
283 (TechnologyCategory::MetaFramework, "ποΈ Meta-Frameworks"),
284 (
285 TechnologyCategory::BackendFramework,
286 "π₯οΈ Backend Frameworks",
287 ),
288 (
289 TechnologyCategory::FrontendFramework,
290 "π¨ Frontend Frameworks",
291 ),
292 (
293 TechnologyCategory::Library(LibraryType::UI),
294 "π¨ UI Libraries",
295 ),
296 (
297 TechnologyCategory::Library(LibraryType::Utility),
298 "π Core Libraries",
299 ),
300 (TechnologyCategory::BuildTool, "π¨ Build Tools"),
301 (TechnologyCategory::PackageManager, "π¦ Package Managers"),
302 (TechnologyCategory::Database, "ποΈ Database & ORM"),
303 (TechnologyCategory::Runtime, "β‘ Runtimes"),
304 (TechnologyCategory::Testing, "π§ͺ Testing"),
305 ];
306
307 for (category, label) in &categories {
308 if let Some(techs) = by_category.get(category) {
309 if !techs.is_empty() {
310 output.push_str(&format!("\n {}:\n", label));
311 for tech in techs {
312 output.push_str(&format!(
313 " β’ {} (confidence: {:.1}%)\n",
314 tech.name,
315 tech.confidence * 100.0
316 ));
317 if let Some(version) = &tech.version {
318 output.push_str(&format!(" Version: {}\n", version));
319 }
320 }
321 }
322 }
323 }
324
325 for (cat, techs) in &by_category {
327 match cat {
328 TechnologyCategory::Library(lib_type) => {
329 let label = match lib_type {
330 LibraryType::StateManagement => "π State Management",
331 LibraryType::DataFetching => "π Data Fetching",
332 LibraryType::Routing => "πΊοΈ Routing",
333 LibraryType::Styling => "π¨ Styling",
334 LibraryType::HttpClient => "π HTTP Clients",
335 LibraryType::Authentication => "π Authentication",
336 LibraryType::Other(_) => "π¦ Other Libraries",
337 _ => continue, };
339
340 if !matches!(lib_type, LibraryType::UI | LibraryType::Utility) && !techs.is_empty()
342 {
343 output.push_str(&format!("\n {}:\n", label));
344 for tech in techs {
345 output.push_str(&format!(
346 " β’ {} (confidence: {:.1}%)\n",
347 tech.name,
348 tech.confidence * 100.0
349 ));
350 if let Some(version) = &tech.version {
351 output.push_str(&format!(" Version: {}\n", version));
352 }
353 }
354 }
355 }
356 _ => {} }
358 }
359
360 output
361}
362
363pub fn display_docker_analysis_detailed_legacy(docker_analysis: &DockerAnalysis) {
365 println!("\n π³ Docker Infrastructure Analysis:");
366
367 if !docker_analysis.dockerfiles.is_empty() {
369 println!(
370 " π Dockerfiles ({}):",
371 docker_analysis.dockerfiles.len()
372 );
373 for dockerfile in &docker_analysis.dockerfiles {
374 println!(" β’ {}", dockerfile.path.display());
375 if let Some(env) = &dockerfile.environment {
376 println!(" Environment: {}", env);
377 }
378 if let Some(base_image) = &dockerfile.base_image {
379 println!(" Base image: {}", base_image);
380 }
381 if !dockerfile.exposed_ports.is_empty() {
382 println!(
383 " Exposed ports: {}",
384 dockerfile
385 .exposed_ports
386 .iter()
387 .map(|p| p.to_string())
388 .collect::<Vec<_>>()
389 .join(", ")
390 );
391 }
392 if dockerfile.is_multistage {
393 println!(
394 " Multi-stage build: {} stages",
395 dockerfile.build_stages.len()
396 );
397 }
398 println!(" Instructions: {}", dockerfile.instruction_count);
399 }
400 }
401
402 if !docker_analysis.compose_files.is_empty() {
404 println!(
405 " π Compose Files ({}):",
406 docker_analysis.compose_files.len()
407 );
408 for compose_file in &docker_analysis.compose_files {
409 println!(" β’ {}", compose_file.path.display());
410 if let Some(env) = &compose_file.environment {
411 println!(" Environment: {}", env);
412 }
413 if let Some(version) = &compose_file.version {
414 println!(" Version: {}", version);
415 }
416 if !compose_file.service_names.is_empty() {
417 println!(
418 " Services: {}",
419 compose_file.service_names.join(", ")
420 );
421 }
422 if !compose_file.networks.is_empty() {
423 println!(" Networks: {}", compose_file.networks.join(", "));
424 }
425 if !compose_file.volumes.is_empty() {
426 println!(" Volumes: {}", compose_file.volumes.join(", "));
427 }
428 }
429 }
430
431 println!(
433 " ποΈ Orchestration Pattern: {:?}",
434 docker_analysis.orchestration_pattern
435 );
436 match docker_analysis.orchestration_pattern {
437 OrchestrationPattern::SingleContainer => {
438 println!(" Simple containerized application");
439 }
440 OrchestrationPattern::DockerCompose => {
441 println!(" Multi-service Docker Compose setup");
442 }
443 OrchestrationPattern::Microservices => {
444 println!(" Microservices architecture with service discovery");
445 }
446 OrchestrationPattern::EventDriven => {
447 println!(" Event-driven architecture with message queues");
448 }
449 OrchestrationPattern::ServiceMesh => {
450 println!(" Service mesh for advanced service communication");
451 }
452 OrchestrationPattern::Mixed => {
453 println!(" Mixed/complex orchestration pattern");
454 }
455 }
456}
457
458pub fn display_docker_analysis_detailed_legacy_to_string(
460 docker_analysis: &DockerAnalysis,
461) -> String {
462 let mut output = String::new();
463
464 output.push_str("\n π³ Docker Infrastructure Analysis:\n");
465
466 if !docker_analysis.dockerfiles.is_empty() {
468 output.push_str(&format!(
469 " π Dockerfiles ({}):\n",
470 docker_analysis.dockerfiles.len()
471 ));
472 for dockerfile in &docker_analysis.dockerfiles {
473 output.push_str(&format!(" β’ {}\n", dockerfile.path.display()));
474 if let Some(env) = &dockerfile.environment {
475 output.push_str(&format!(" Environment: {}\n", env));
476 }
477 if let Some(base_image) = &dockerfile.base_image {
478 output.push_str(&format!(" Base image: {}\n", base_image));
479 }
480 if !dockerfile.exposed_ports.is_empty() {
481 output.push_str(&format!(
482 " Exposed ports: {}\n",
483 dockerfile
484 .exposed_ports
485 .iter()
486 .map(|p| p.to_string())
487 .collect::<Vec<_>>()
488 .join(", ")
489 ));
490 }
491 if dockerfile.is_multistage {
492 output.push_str(&format!(
493 " Multi-stage build: {} stages\n",
494 dockerfile.build_stages.len()
495 ));
496 }
497 output.push_str(&format!(
498 " Instructions: {}\n",
499 dockerfile.instruction_count
500 ));
501 }
502 }
503
504 if !docker_analysis.compose_files.is_empty() {
506 output.push_str(&format!(
507 " π Compose Files ({}):\n",
508 docker_analysis.compose_files.len()
509 ));
510 for compose_file in &docker_analysis.compose_files {
511 output.push_str(&format!(" β’ {}\n", compose_file.path.display()));
512 if let Some(env) = &compose_file.environment {
513 output.push_str(&format!(" Environment: {}\n", env));
514 }
515 if let Some(version) = &compose_file.version {
516 output.push_str(&format!(" Version: {}\n", version));
517 }
518 if !compose_file.service_names.is_empty() {
519 output.push_str(&format!(
520 " Services: {}\n",
521 compose_file.service_names.join(", ")
522 ));
523 }
524 if !compose_file.networks.is_empty() {
525 output.push_str(&format!(
526 " Networks: {}\n",
527 compose_file.networks.join(", ")
528 ));
529 }
530 if !compose_file.volumes.is_empty() {
531 output.push_str(&format!(
532 " Volumes: {}\n",
533 compose_file.volumes.join(", ")
534 ));
535 }
536 }
537 }
538
539 output.push_str(&format!(
541 " ποΈ Orchestration Pattern: {:?}\n",
542 docker_analysis.orchestration_pattern
543 ));
544 match docker_analysis.orchestration_pattern {
545 OrchestrationPattern::SingleContainer => {
546 output.push_str(" Simple containerized application\n");
547 }
548 OrchestrationPattern::DockerCompose => {
549 output.push_str(" Multi-service Docker Compose setup\n");
550 }
551 OrchestrationPattern::Microservices => {
552 output.push_str(" Microservices architecture with service discovery\n");
553 }
554 OrchestrationPattern::EventDriven => {
555 output.push_str(" Event-driven architecture with message queues\n");
556 }
557 OrchestrationPattern::ServiceMesh => {
558 output.push_str(" Service mesh for advanced service communication\n");
559 }
560 OrchestrationPattern::Mixed => {
561 output.push_str(" Mixed/complex orchestration pattern\n");
562 }
563 }
564
565 output
566}