syncable_cli/analyzer/display/
helpers.rs1use crate::analyzer::{
4 ProjectCategory, ArchitecturePattern, DetectedTechnology,
5 TechnologyCategory, LibraryType, DockerAnalysis, OrchestrationPattern
6};
7use crate::analyzer::display::BoxDrawer;
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!(" π This is a microservices architecture with multiple independent services");
51 }
52 ArchitecturePattern::ApiFirst => {
53 println!(" π This is an API-first architecture focused on service interfaces");
54 }
55 ArchitecturePattern::EventDriven => {
56 println!(" π‘ This is an event-driven architecture with decoupled components");
57 }
58 ArchitecturePattern::Mixed => {
59 println!(" π This is a mixed architecture combining multiple patterns");
60 }
61 }
62}
63
64pub fn display_architecture_description_to_string(pattern: &ArchitecturePattern) -> String {
66 match pattern {
67 ArchitecturePattern::Monolithic => {
68 " π¦ This is a single, self-contained application\n".to_string()
69 }
70 ArchitecturePattern::Fullstack => {
71 " π This is a full-stack application with separate frontend and backend\n".to_string()
72 }
73 ArchitecturePattern::Microservices => {
74 " π This is a microservices architecture with multiple independent services\n".to_string()
75 }
76 ArchitecturePattern::ApiFirst => {
77 " π This is an API-first architecture focused on service interfaces\n".to_string()
78 }
79 ArchitecturePattern::EventDriven => {
80 " π‘ This is an event-driven architecture with decoupled components\n".to_string()
81 }
82 ArchitecturePattern::Mixed => {
83 " π This is a mixed architecture combining multiple patterns\n".to_string()
84 }
85 }
86}
87
88pub fn get_main_technologies(technologies: &[DetectedTechnology]) -> String {
90 let primary = technologies.iter().find(|t| t.is_primary);
91 let frameworks: Vec<_> = technologies.iter()
92 .filter(|t| matches!(t.category, TechnologyCategory::FrontendFramework | TechnologyCategory::MetaFramework))
93 .take(2)
94 .collect();
95
96 let mut result = Vec::new();
97
98 if let Some(p) = primary {
99 result.push(p.name.clone());
100 }
101
102 for f in frameworks {
103 if Some(&f.name) != primary.map(|p| &p.name) {
104 result.push(f.name.clone());
105 }
106 }
107
108 if result.is_empty() {
109 "-".to_string()
110 } else {
111 result.join(", ")
112 }
113}
114
115pub fn add_confidence_bar_to_drawer(score: f32, box_drawer: &mut BoxDrawer) {
117 let percentage = (score * 100.0) as u8;
118 let bar_width = 20;
119 let filled = ((score * bar_width as f32) as usize).min(bar_width);
120
121 let bar = format!("{}{}",
122 "β".repeat(filled).green(),
123 "β".repeat(bar_width - filled).dimmed()
124 );
125
126 let color = if percentage >= 80 {
127 "green"
128 } else if percentage >= 60 {
129 "yellow"
130 } else {
131 "red"
132 };
133
134 let confidence_info = format!("{} {}", bar, format!("{:.0}%", percentage).color(color));
135 box_drawer.add_line("Confidence:", &confidence_info, true);
136}
137
138pub fn display_technologies_detailed_legacy(technologies: &[DetectedTechnology]) {
140 let mut by_category: std::collections::HashMap<&TechnologyCategory, Vec<&DetectedTechnology>> = std::collections::HashMap::new();
142
143 for tech in technologies {
144 by_category.entry(&tech.category).or_insert_with(Vec::new).push(tech);
145 }
146
147 if let Some(primary) = technologies.iter().find(|t| t.is_primary) {
149 println!("\nπ οΈ Technology Stack:");
150 println!(" π― PRIMARY: {} (confidence: {:.1}%)", primary.name, primary.confidence * 100.0);
151 println!(" Architecture driver for this project");
152 }
153
154 let categories = [
156 (TechnologyCategory::MetaFramework, "ποΈ Meta-Frameworks"),
157 (TechnologyCategory::BackendFramework, "π₯οΈ Backend Frameworks"),
158 (TechnologyCategory::FrontendFramework, "π¨ Frontend Frameworks"),
159 (TechnologyCategory::Library(LibraryType::UI), "π¨ UI Libraries"),
160 (TechnologyCategory::Library(LibraryType::Utility), "π Core Libraries"),
161 (TechnologyCategory::BuildTool, "π¨ Build Tools"),
162 (TechnologyCategory::PackageManager, "π¦ Package Managers"),
163 (TechnologyCategory::Database, "ποΈ Database & ORM"),
164 (TechnologyCategory::Runtime, "β‘ Runtimes"),
165 (TechnologyCategory::Testing, "π§ͺ Testing"),
166 ];
167
168 for (category, label) in &categories {
169 if let Some(techs) = by_category.get(category) {
170 if !techs.is_empty() {
171 println!("\n {}:", label);
172 for tech in techs {
173 println!(" β’ {} (confidence: {:.1}%)", tech.name, tech.confidence * 100.0);
174 if let Some(version) = &tech.version {
175 println!(" Version: {}", version);
176 }
177 }
178 }
179 }
180 }
181
182 for (cat, techs) in &by_category {
184 match cat {
185 TechnologyCategory::Library(lib_type) => {
186 let label = match lib_type {
187 LibraryType::StateManagement => "π State Management",
188 LibraryType::DataFetching => "π Data Fetching",
189 LibraryType::Routing => "πΊοΈ Routing",
190 LibraryType::Styling => "π¨ Styling",
191 LibraryType::HttpClient => "π HTTP Clients",
192 LibraryType::Authentication => "π Authentication",
193 LibraryType::Other(_) => "π¦ Other Libraries",
194 _ => continue, };
196
197 if !matches!(lib_type, LibraryType::UI | LibraryType::Utility) && !techs.is_empty() {
199 println!("\n {}:", label);
200 for tech in techs {
201 println!(" β’ {} (confidence: {:.1}%)", tech.name, tech.confidence * 100.0);
202 if let Some(version) = &tech.version {
203 println!(" Version: {}", version);
204 }
205 }
206 }
207 }
208 _ => {} }
210 }
211}
212
213pub fn display_technologies_detailed_legacy_to_string(technologies: &[DetectedTechnology]) -> String {
215 let mut output = String::new();
216
217 let mut by_category: std::collections::HashMap<&TechnologyCategory, Vec<&DetectedTechnology>> = std::collections::HashMap::new();
219
220 for tech in technologies {
221 by_category.entry(&tech.category).or_insert_with(Vec::new).push(tech);
222 }
223
224 if let Some(primary) = technologies.iter().find(|t| t.is_primary) {
226 output.push_str("\nπ οΈ Technology Stack:\n");
227 output.push_str(&format!(" π― PRIMARY: {} (confidence: {:.1}%)\n", primary.name, primary.confidence * 100.0));
228 output.push_str(" Architecture driver for this project\n");
229 }
230
231 let categories = [
233 (TechnologyCategory::MetaFramework, "ποΈ Meta-Frameworks"),
234 (TechnologyCategory::BackendFramework, "π₯οΈ Backend Frameworks"),
235 (TechnologyCategory::FrontendFramework, "π¨ Frontend Frameworks"),
236 (TechnologyCategory::Library(LibraryType::UI), "π¨ UI Libraries"),
237 (TechnologyCategory::Library(LibraryType::Utility), "π Core Libraries"),
238 (TechnologyCategory::BuildTool, "π¨ Build Tools"),
239 (TechnologyCategory::PackageManager, "π¦ Package Managers"),
240 (TechnologyCategory::Database, "ποΈ Database & ORM"),
241 (TechnologyCategory::Runtime, "β‘ Runtimes"),
242 (TechnologyCategory::Testing, "π§ͺ Testing"),
243 ];
244
245 for (category, label) in &categories {
246 if let Some(techs) = by_category.get(category) {
247 if !techs.is_empty() {
248 output.push_str(&format!("\n {}:\n", label));
249 for tech in techs {
250 output.push_str(&format!(" β’ {} (confidence: {:.1}%)\n", tech.name, tech.confidence * 100.0));
251 if let Some(version) = &tech.version {
252 output.push_str(&format!(" Version: {}\n", version));
253 }
254 }
255 }
256 }
257 }
258
259 for (cat, techs) in &by_category {
261 match cat {
262 TechnologyCategory::Library(lib_type) => {
263 let label = match lib_type {
264 LibraryType::StateManagement => "π State Management",
265 LibraryType::DataFetching => "π Data Fetching",
266 LibraryType::Routing => "πΊοΈ Routing",
267 LibraryType::Styling => "π¨ Styling",
268 LibraryType::HttpClient => "π HTTP Clients",
269 LibraryType::Authentication => "π Authentication",
270 LibraryType::Other(_) => "π¦ Other Libraries",
271 _ => continue, };
273
274 if !matches!(lib_type, LibraryType::UI | LibraryType::Utility) && !techs.is_empty() {
276 output.push_str(&format!("\n {}:\n", label));
277 for tech in techs {
278 output.push_str(&format!(" β’ {} (confidence: {:.1}%)\n", tech.name, tech.confidence * 100.0));
279 if let Some(version) = &tech.version {
280 output.push_str(&format!(" Version: {}\n", version));
281 }
282 }
283 }
284 }
285 _ => {} }
287 }
288
289 output
290}
291
292pub fn display_docker_analysis_detailed_legacy(docker_analysis: &DockerAnalysis) {
294 println!("\n π³ Docker Infrastructure Analysis:");
295
296 if !docker_analysis.dockerfiles.is_empty() {
298 println!(" π Dockerfiles ({}):", docker_analysis.dockerfiles.len());
299 for dockerfile in &docker_analysis.dockerfiles {
300 println!(" β’ {}", dockerfile.path.display());
301 if let Some(env) = &dockerfile.environment {
302 println!(" Environment: {}", env);
303 }
304 if let Some(base_image) = &dockerfile.base_image {
305 println!(" Base image: {}", base_image);
306 }
307 if !dockerfile.exposed_ports.is_empty() {
308 println!(" Exposed ports: {}",
309 dockerfile.exposed_ports.iter().map(|p| p.to_string()).collect::<Vec<_>>().join(", "));
310 }
311 if dockerfile.is_multistage {
312 println!(" Multi-stage build: {} stages", dockerfile.build_stages.len());
313 }
314 println!(" Instructions: {}", dockerfile.instruction_count);
315 }
316 }
317
318 if !docker_analysis.compose_files.is_empty() {
320 println!(" π Compose Files ({}):", docker_analysis.compose_files.len());
321 for compose_file in &docker_analysis.compose_files {
322 println!(" β’ {}", compose_file.path.display());
323 if let Some(env) = &compose_file.environment {
324 println!(" Environment: {}", env);
325 }
326 if let Some(version) = &compose_file.version {
327 println!(" Version: {}", version);
328 }
329 if !compose_file.service_names.is_empty() {
330 println!(" Services: {}", compose_file.service_names.join(", "));
331 }
332 if !compose_file.networks.is_empty() {
333 println!(" Networks: {}", compose_file.networks.join(", "));
334 }
335 if !compose_file.volumes.is_empty() {
336 println!(" Volumes: {}", compose_file.volumes.join(", "));
337 }
338 }
339 }
340
341 println!(" ποΈ Orchestration Pattern: {:?}", docker_analysis.orchestration_pattern);
343 match docker_analysis.orchestration_pattern {
344 OrchestrationPattern::SingleContainer => {
345 println!(" Simple containerized application");
346 }
347 OrchestrationPattern::DockerCompose => {
348 println!(" Multi-service Docker Compose setup");
349 }
350 OrchestrationPattern::Microservices => {
351 println!(" Microservices architecture with service discovery");
352 }
353 OrchestrationPattern::EventDriven => {
354 println!(" Event-driven architecture with message queues");
355 }
356 OrchestrationPattern::ServiceMesh => {
357 println!(" Service mesh for advanced service communication");
358 }
359 OrchestrationPattern::Mixed => {
360 println!(" Mixed/complex orchestration pattern");
361 }
362 }
363}
364
365pub fn display_docker_analysis_detailed_legacy_to_string(docker_analysis: &DockerAnalysis) -> String {
367 let mut output = String::new();
368
369 output.push_str("\n π³ Docker Infrastructure Analysis:\n");
370
371 if !docker_analysis.dockerfiles.is_empty() {
373 output.push_str(&format!(" π Dockerfiles ({}):\n", docker_analysis.dockerfiles.len()));
374 for dockerfile in &docker_analysis.dockerfiles {
375 output.push_str(&format!(" β’ {}\n", dockerfile.path.display()));
376 if let Some(env) = &dockerfile.environment {
377 output.push_str(&format!(" Environment: {}\n", env));
378 }
379 if let Some(base_image) = &dockerfile.base_image {
380 output.push_str(&format!(" Base image: {}\n", base_image));
381 }
382 if !dockerfile.exposed_ports.is_empty() {
383 output.push_str(&format!(" Exposed ports: {}\n",
384 dockerfile.exposed_ports.iter().map(|p| p.to_string()).collect::<Vec<_>>().join(", ")));
385 }
386 if dockerfile.is_multistage {
387 output.push_str(&format!(" Multi-stage build: {} stages\n", dockerfile.build_stages.len()));
388 }
389 output.push_str(&format!(" Instructions: {}\n", dockerfile.instruction_count));
390 }
391 }
392
393 if !docker_analysis.compose_files.is_empty() {
395 output.push_str(&format!(" π Compose Files ({}):\n", docker_analysis.compose_files.len()));
396 for compose_file in &docker_analysis.compose_files {
397 output.push_str(&format!(" β’ {}\n", compose_file.path.display()));
398 if let Some(env) = &compose_file.environment {
399 output.push_str(&format!(" Environment: {}\n", env));
400 }
401 if let Some(version) = &compose_file.version {
402 output.push_str(&format!(" Version: {}\n", version));
403 }
404 if !compose_file.service_names.is_empty() {
405 output.push_str(&format!(" Services: {}\n", compose_file.service_names.join(", ")));
406 }
407 if !compose_file.networks.is_empty() {
408 output.push_str(&format!(" Networks: {}\n", compose_file.networks.join(", ")));
409 }
410 if !compose_file.volumes.is_empty() {
411 output.push_str(&format!(" Volumes: {}\n", compose_file.volumes.join(", ")));
412 }
413 }
414 }
415
416 output.push_str(&format!(" ποΈ Orchestration Pattern: {:?}\n", docker_analysis.orchestration_pattern));
418 match docker_analysis.orchestration_pattern {
419 OrchestrationPattern::SingleContainer => {
420 output.push_str(" Simple containerized application\n");
421 }
422 OrchestrationPattern::DockerCompose => {
423 output.push_str(" Multi-service Docker Compose setup\n");
424 }
425 OrchestrationPattern::Microservices => {
426 output.push_str(" Microservices architecture with service discovery\n");
427 }
428 OrchestrationPattern::EventDriven => {
429 output.push_str(" Event-driven architecture with message queues\n");
430 }
431 OrchestrationPattern::ServiceMesh => {
432 output.push_str(" Service mesh for advanced service communication\n");
433 }
434 OrchestrationPattern::Mixed => {
435 output.push_str(" Mixed/complex orchestration pattern\n");
436 }
437 }
438
439 output
440}