1use crate::error::Result;
10use chrono::Utc;
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use std::path::{Path, PathBuf};
14
15pub mod dependency_parser;
16pub mod framework_detector;
17pub mod language_detector;
18pub mod project_context;
19pub mod vulnerability_checker;
20pub mod security_analyzer;
21pub mod tool_installer;
22pub mod monorepo_detector;
23pub mod docker_analyzer;
24pub mod display;
25
26pub use dependency_parser::{
28 DependencyInfo, DependencyAnalysis, DetailedDependencyMap,
29 Vulnerability, VulnerabilitySeverity
30};
31
32pub use security_analyzer::{
34 SecurityAnalyzer, SecurityReport, SecurityFinding, SecuritySeverity,
35 SecurityCategory, ComplianceStatus, SecurityAnalysisConfig
36};
37
38pub use monorepo_detector::{
40 MonorepoDetectionConfig, analyze_monorepo, analyze_monorepo_with_config
41};
42
43pub use docker_analyzer::{
45 DockerAnalysis, DockerfileInfo, ComposeFileInfo, DockerService,
46 OrchestrationPattern, NetworkingConfig, DockerEnvironment,
47 analyze_docker_infrastructure
48};
49
50#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
52pub struct DetectedLanguage {
53 pub name: String,
54 pub version: Option<String>,
55 pub confidence: f32,
56 pub files: Vec<PathBuf>,
57 pub main_dependencies: Vec<String>,
58 pub dev_dependencies: Vec<String>,
59 pub package_manager: Option<String>,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
64pub enum TechnologyCategory {
65 MetaFramework,
67 FrontendFramework,
69 BackendFramework,
71 Library(LibraryType),
73 BuildTool,
75 Database,
77 Testing,
79 Runtime,
81 PackageManager,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
87pub enum LibraryType {
88 UI,
90 StateManagement,
92 DataFetching,
94 Routing,
96 Styling,
98 Utility,
100 HttpClient,
102 Authentication,
104 Other(String),
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
110pub struct DetectedTechnology {
111 pub name: String,
112 pub version: Option<String>,
113 pub category: TechnologyCategory,
114 pub confidence: f32,
115 pub requires: Vec<String>,
117 pub conflicts_with: Vec<String>,
119 pub is_primary: bool,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
125pub struct ServiceAnalysis {
126 pub name: String,
127 pub path: PathBuf,
128 pub languages: Vec<DetectedLanguage>,
129 pub technologies: Vec<DetectedTechnology>,
130 pub entry_points: Vec<EntryPoint>,
131 pub ports: Vec<Port>,
132 pub environment_variables: Vec<EnvVar>,
133 pub build_scripts: Vec<BuildScript>,
134 pub service_type: ProjectType,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
139pub struct EntryPoint {
140 pub file: PathBuf,
141 pub function: Option<String>,
142 pub command: Option<String>,
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
147pub struct Port {
148 pub number: u16,
149 pub protocol: Protocol,
150 pub description: Option<String>,
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
154pub enum Protocol {
155 Tcp,
156 Udp,
157 Http,
158 Https,
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
163pub struct EnvVar {
164 pub name: String,
165 pub default_value: Option<String>,
166 pub required: bool,
167 pub description: Option<String>,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
172pub enum ProjectType {
173 WebApplication,
174 ApiService,
175 CliTool,
176 Library,
177 MobileApp,
178 DesktopApp,
179 Microservice,
180 StaticSite,
181 Hybrid, Unknown,
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
187pub struct BuildScript {
188 pub name: String,
189 pub command: String,
190 pub description: Option<String>,
191 pub is_default: bool,
192}
193
194pub type DependencyMap = HashMap<String, String>;
196
197#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
199pub enum ArchitectureType {
200 Monolithic,
202 Microservices,
204 Hybrid,
206}
207
208pub type DetectedFramework = DetectedTechnology;
210
211#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
213pub struct ProjectAnalysis {
214 pub project_root: PathBuf,
215 pub languages: Vec<DetectedLanguage>,
216 pub technologies: Vec<DetectedTechnology>,
218 #[deprecated(note = "Use technologies field instead")]
220 pub frameworks: Vec<DetectedFramework>,
221 pub dependencies: DependencyMap,
222 pub entry_points: Vec<EntryPoint>,
223 pub ports: Vec<Port>,
224 pub environment_variables: Vec<EnvVar>,
225 pub project_type: ProjectType,
226 pub build_scripts: Vec<BuildScript>,
227 pub services: Vec<ServiceAnalysis>,
229 pub architecture_type: ArchitectureType,
231 pub docker_analysis: Option<DockerAnalysis>,
233 pub analysis_metadata: AnalysisMetadata,
234}
235
236#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
238pub struct AnalysisMetadata {
239 pub timestamp: String,
240 pub analyzer_version: String,
241 pub analysis_duration_ms: u64,
242 pub files_analyzed: usize,
243 pub confidence_score: f32,
244}
245
246#[derive(Debug, Clone)]
248pub struct AnalysisConfig {
249 pub include_dev_dependencies: bool,
250 pub deep_analysis: bool,
251 pub ignore_patterns: Vec<String>,
252 pub max_file_size: usize,
253}
254
255impl Default for AnalysisConfig {
256 fn default() -> Self {
257 Self {
258 include_dev_dependencies: false,
259 deep_analysis: true,
260 ignore_patterns: vec![
261 "node_modules".to_string(),
262 ".git".to_string(),
263 "target".to_string(),
264 "build".to_string(),
265 ".next".to_string(),
266 "dist".to_string(),
267 ],
268 max_file_size: 1024 * 1024, }
270 }
271}
272
273#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
275pub struct ProjectInfo {
276 pub path: PathBuf,
278 pub name: String,
280 pub project_category: ProjectCategory,
282 pub analysis: ProjectAnalysis,
284}
285
286#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
288pub enum ProjectCategory {
289 Frontend,
290 Backend,
291 Api,
292 Service,
293 Library,
294 Tool,
295 Documentation,
296 Infrastructure,
297 Unknown,
298}
299
300#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
302pub struct MonorepoAnalysis {
303 pub root_path: PathBuf,
305 pub is_monorepo: bool,
307 pub projects: Vec<ProjectInfo>,
309 pub metadata: AnalysisMetadata,
311 pub technology_summary: TechnologySummary,
313}
314
315#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
317pub struct TechnologySummary {
318 pub languages: Vec<String>,
319 pub frameworks: Vec<String>,
320 pub databases: Vec<String>,
321 pub total_projects: usize,
322 pub architecture_pattern: ArchitecturePattern,
323}
324
325#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
327pub enum ArchitecturePattern {
328 Monolithic,
330 Fullstack,
332 Microservices,
334 ApiFirst,
336 EventDriven,
338 Mixed,
340}
341
342pub fn analyze_project(path: &Path) -> Result<ProjectAnalysis> {
362 analyze_project_with_config(path, &AnalysisConfig::default())
363}
364
365pub fn analyze_project_with_config(path: &Path, config: &AnalysisConfig) -> Result<ProjectAnalysis> {
367 let start_time = std::time::Instant::now();
368
369 let project_root = crate::common::file_utils::validate_project_path(path)?;
371
372 log::info!("Starting analysis of project: {}", project_root.display());
373
374 let files = crate::common::file_utils::collect_project_files(&project_root, config)?;
376 log::debug!("Found {} files to analyze", files.len());
377
378 let languages = language_detector::detect_languages(&files, config)?;
380 let frameworks = framework_detector::detect_frameworks(&project_root, &languages, config)?;
381 let dependencies = dependency_parser::parse_dependencies(&project_root, &languages, config)?;
382 let context = project_context::analyze_context(&project_root, &languages, &frameworks, config)?;
383
384 let docker_analysis = analyze_docker_infrastructure(&project_root).ok();
386
387 let duration = start_time.elapsed();
388 let confidence = calculate_confidence_score(&languages, &frameworks);
389
390 #[allow(deprecated)]
391 let analysis = ProjectAnalysis {
392 project_root,
393 languages,
394 technologies: frameworks.clone(), frameworks, dependencies,
397 entry_points: context.entry_points,
398 ports: context.ports,
399 environment_variables: context.environment_variables,
400 project_type: context.project_type,
401 build_scripts: context.build_scripts,
402 services: vec![], architecture_type: ArchitectureType::Monolithic, docker_analysis,
405 analysis_metadata: AnalysisMetadata {
406 timestamp: Utc::now().to_rfc3339(),
407 analyzer_version: env!("CARGO_PKG_VERSION").to_string(),
408 analysis_duration_ms: duration.as_millis() as u64,
409 files_analyzed: files.len(),
410 confidence_score: confidence,
411 },
412 };
413
414 log::info!("Analysis completed in {}ms", duration.as_millis());
415 Ok(analysis)
416}
417
418fn calculate_confidence_score(
420 languages: &[DetectedLanguage],
421 frameworks: &[DetectedFramework],
422) -> f32 {
423 if languages.is_empty() {
424 return 0.0;
425 }
426
427 let lang_confidence: f32 = languages.iter().map(|l| l.confidence).sum::<f32>() / languages.len() as f32;
428 let framework_confidence: f32 = if frameworks.is_empty() {
429 0.5 } else {
431 frameworks.iter().map(|f| f.confidence).sum::<f32>() / frameworks.len() as f32
432 };
433
434 (lang_confidence * 0.7 + framework_confidence * 0.3).min(1.0)
435}
436
437#[cfg(test)]
438mod tests {
439 use super::*;
440
441 #[test]
442 fn test_confidence_calculation() {
443 let languages = vec![
444 DetectedLanguage {
445 name: "Rust".to_string(),
446 version: Some("1.70.0".to_string()),
447 confidence: 0.9,
448 files: vec![],
449 main_dependencies: vec!["serde".to_string(), "tokio".to_string()],
450 dev_dependencies: vec!["assert_cmd".to_string()],
451 package_manager: Some("cargo".to_string()),
452 }
453 ];
454
455 let technologies = vec![
456 DetectedTechnology {
457 name: "Actix Web".to_string(),
458 version: Some("4.0".to_string()),
459 category: TechnologyCategory::BackendFramework,
460 confidence: 0.8,
461 requires: vec!["serde".to_string(), "tokio".to_string()],
462 conflicts_with: vec![],
463 is_primary: true,
464 }
465 ];
466
467 let frameworks = technologies.clone(); let score = calculate_confidence_score(&languages, &frameworks);
470 assert!(score > 0.8);
471 assert!(score <= 1.0);
472 }
473
474 #[test]
475 fn test_empty_analysis() {
476 let languages = vec![];
477 let frameworks = vec![];
478 let score = calculate_confidence_score(&languages, &frameworks);
479 assert_eq!(score, 0.0);
480 }
481}