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;
24
25pub use dependency_parser::{
27 DependencyInfo, DependencyAnalysis, DetailedDependencyMap,
28 Vulnerability, VulnerabilitySeverity
29};
30
31pub use security_analyzer::{
33 SecurityAnalyzer, SecurityReport, SecurityFinding, SecuritySeverity,
34 SecurityCategory, ComplianceStatus, SecurityAnalysisConfig
35};
36
37pub use monorepo_detector::{
39 MonorepoDetectionConfig, analyze_monorepo, analyze_monorepo_with_config
40};
41
42pub use docker_analyzer::{
44 DockerAnalysis, DockerfileInfo, ComposeFileInfo, DockerService,
45 OrchestrationPattern, NetworkingConfig, DockerEnvironment,
46 analyze_docker_infrastructure
47};
48
49#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
51pub struct DetectedLanguage {
52 pub name: String,
53 pub version: Option<String>,
54 pub confidence: f32,
55 pub files: Vec<PathBuf>,
56 pub main_dependencies: Vec<String>,
57 pub dev_dependencies: Vec<String>,
58 pub package_manager: Option<String>,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
63pub enum TechnologyCategory {
64 MetaFramework,
66 FrontendFramework,
68 BackendFramework,
70 Library(LibraryType),
72 BuildTool,
74 Database,
76 Testing,
78 Runtime,
80 PackageManager,
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
86pub enum LibraryType {
87 UI,
89 StateManagement,
91 DataFetching,
93 Routing,
95 Styling,
97 Utility,
99 HttpClient,
101 Authentication,
103 Other(String),
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
109pub struct DetectedTechnology {
110 pub name: String,
111 pub version: Option<String>,
112 pub category: TechnologyCategory,
113 pub confidence: f32,
114 pub requires: Vec<String>,
116 pub conflicts_with: Vec<String>,
118 pub is_primary: bool,
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
124pub struct ServiceAnalysis {
125 pub name: String,
126 pub path: PathBuf,
127 pub languages: Vec<DetectedLanguage>,
128 pub technologies: Vec<DetectedTechnology>,
129 pub entry_points: Vec<EntryPoint>,
130 pub ports: Vec<Port>,
131 pub environment_variables: Vec<EnvVar>,
132 pub build_scripts: Vec<BuildScript>,
133 pub service_type: ProjectType,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
138pub struct EntryPoint {
139 pub file: PathBuf,
140 pub function: Option<String>,
141 pub command: Option<String>,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
146pub struct Port {
147 pub number: u16,
148 pub protocol: Protocol,
149 pub description: Option<String>,
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
153pub enum Protocol {
154 Tcp,
155 Udp,
156 Http,
157 Https,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
162pub struct EnvVar {
163 pub name: String,
164 pub default_value: Option<String>,
165 pub required: bool,
166 pub description: Option<String>,
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
171pub enum ProjectType {
172 WebApplication,
173 ApiService,
174 CliTool,
175 Library,
176 MobileApp,
177 DesktopApp,
178 Microservice,
179 StaticSite,
180 Hybrid, Unknown,
182}
183
184#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
186pub struct BuildScript {
187 pub name: String,
188 pub command: String,
189 pub description: Option<String>,
190 pub is_default: bool,
191}
192
193pub type DependencyMap = HashMap<String, String>;
195
196#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
198pub enum ArchitectureType {
199 Monolithic,
201 Microservices,
203 Hybrid,
205}
206
207pub type DetectedFramework = DetectedTechnology;
209
210#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
212pub struct ProjectAnalysis {
213 pub project_root: PathBuf,
214 pub languages: Vec<DetectedLanguage>,
215 pub technologies: Vec<DetectedTechnology>,
217 #[deprecated(note = "Use technologies field instead")]
219 pub frameworks: Vec<DetectedFramework>,
220 pub dependencies: DependencyMap,
221 pub entry_points: Vec<EntryPoint>,
222 pub ports: Vec<Port>,
223 pub environment_variables: Vec<EnvVar>,
224 pub project_type: ProjectType,
225 pub build_scripts: Vec<BuildScript>,
226 pub services: Vec<ServiceAnalysis>,
228 pub architecture_type: ArchitectureType,
230 pub docker_analysis: Option<DockerAnalysis>,
232 pub analysis_metadata: AnalysisMetadata,
233}
234
235#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
237pub struct AnalysisMetadata {
238 pub timestamp: String,
239 pub analyzer_version: String,
240 pub analysis_duration_ms: u64,
241 pub files_analyzed: usize,
242 pub confidence_score: f32,
243}
244
245#[derive(Debug, Clone)]
247pub struct AnalysisConfig {
248 pub include_dev_dependencies: bool,
249 pub deep_analysis: bool,
250 pub ignore_patterns: Vec<String>,
251 pub max_file_size: usize,
252}
253
254impl Default for AnalysisConfig {
255 fn default() -> Self {
256 Self {
257 include_dev_dependencies: false,
258 deep_analysis: true,
259 ignore_patterns: vec![
260 "node_modules".to_string(),
261 ".git".to_string(),
262 "target".to_string(),
263 "build".to_string(),
264 ".next".to_string(),
265 "dist".to_string(),
266 ],
267 max_file_size: 1024 * 1024, }
269 }
270}
271
272#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
274pub struct ProjectInfo {
275 pub path: PathBuf,
277 pub name: String,
279 pub project_category: ProjectCategory,
281 pub analysis: ProjectAnalysis,
283}
284
285#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
287pub enum ProjectCategory {
288 Frontend,
289 Backend,
290 Api,
291 Service,
292 Library,
293 Tool,
294 Documentation,
295 Infrastructure,
296 Unknown,
297}
298
299#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
301pub struct MonorepoAnalysis {
302 pub root_path: PathBuf,
304 pub is_monorepo: bool,
306 pub projects: Vec<ProjectInfo>,
308 pub metadata: AnalysisMetadata,
310 pub technology_summary: TechnologySummary,
312}
313
314#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
316pub struct TechnologySummary {
317 pub languages: Vec<String>,
318 pub frameworks: Vec<String>,
319 pub databases: Vec<String>,
320 pub total_projects: usize,
321 pub architecture_pattern: ArchitecturePattern,
322}
323
324#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
326pub enum ArchitecturePattern {
327 Monolithic,
329 Fullstack,
331 Microservices,
333 ApiFirst,
335 EventDriven,
337 Mixed,
339}
340
341pub fn analyze_project(path: &Path) -> Result<ProjectAnalysis> {
361 analyze_project_with_config(path, &AnalysisConfig::default())
362}
363
364pub fn analyze_project_with_config(path: &Path, config: &AnalysisConfig) -> Result<ProjectAnalysis> {
366 let start_time = std::time::Instant::now();
367
368 let project_root = crate::common::file_utils::validate_project_path(path)?;
370
371 log::info!("Starting analysis of project: {}", project_root.display());
372
373 let files = crate::common::file_utils::collect_project_files(&project_root, config)?;
375 log::debug!("Found {} files to analyze", files.len());
376
377 let languages = language_detector::detect_languages(&files, config)?;
379 let frameworks = framework_detector::detect_frameworks(&project_root, &languages, config)?;
380 let dependencies = dependency_parser::parse_dependencies(&project_root, &languages, config)?;
381 let context = project_context::analyze_context(&project_root, &languages, &frameworks, config)?;
382
383 let docker_analysis = analyze_docker_infrastructure(&project_root).ok();
385
386 let duration = start_time.elapsed();
387 let confidence = calculate_confidence_score(&languages, &frameworks);
388
389 #[allow(deprecated)]
390 let analysis = ProjectAnalysis {
391 project_root,
392 languages,
393 technologies: frameworks.clone(), frameworks, dependencies,
396 entry_points: context.entry_points,
397 ports: context.ports,
398 environment_variables: context.environment_variables,
399 project_type: context.project_type,
400 build_scripts: context.build_scripts,
401 services: vec![], architecture_type: ArchitectureType::Monolithic, docker_analysis,
404 analysis_metadata: AnalysisMetadata {
405 timestamp: Utc::now().to_rfc3339(),
406 analyzer_version: env!("CARGO_PKG_VERSION").to_string(),
407 analysis_duration_ms: duration.as_millis() as u64,
408 files_analyzed: files.len(),
409 confidence_score: confidence,
410 },
411 };
412
413 log::info!("Analysis completed in {}ms", duration.as_millis());
414 Ok(analysis)
415}
416
417fn calculate_confidence_score(
419 languages: &[DetectedLanguage],
420 frameworks: &[DetectedFramework],
421) -> f32 {
422 if languages.is_empty() {
423 return 0.0;
424 }
425
426 let lang_confidence: f32 = languages.iter().map(|l| l.confidence).sum::<f32>() / languages.len() as f32;
427 let framework_confidence: f32 = if frameworks.is_empty() {
428 0.5 } else {
430 frameworks.iter().map(|f| f.confidence).sum::<f32>() / frameworks.len() as f32
431 };
432
433 (lang_confidence * 0.7 + framework_confidence * 0.3).min(1.0)
434}
435
436#[cfg(test)]
437mod tests {
438 use super::*;
439
440 #[test]
441 fn test_confidence_calculation() {
442 let languages = vec![
443 DetectedLanguage {
444 name: "Rust".to_string(),
445 version: Some("1.70.0".to_string()),
446 confidence: 0.9,
447 files: vec![],
448 main_dependencies: vec!["serde".to_string(), "tokio".to_string()],
449 dev_dependencies: vec!["assert_cmd".to_string()],
450 package_manager: Some("cargo".to_string()),
451 }
452 ];
453
454 let technologies = vec![
455 DetectedTechnology {
456 name: "Actix Web".to_string(),
457 version: Some("4.0".to_string()),
458 category: TechnologyCategory::BackendFramework,
459 confidence: 0.8,
460 requires: vec!["serde".to_string(), "tokio".to_string()],
461 conflicts_with: vec![],
462 is_primary: true,
463 }
464 ];
465
466 let frameworks = technologies.clone(); let score = calculate_confidence_score(&languages, &frameworks);
469 assert!(score > 0.8);
470 assert!(score <= 1.0);
471 }
472
473 #[test]
474 fn test_empty_analysis() {
475 let languages = vec![];
476 let frameworks = vec![];
477 let score = calculate_confidence_score(&languages, &frameworks);
478 assert_eq!(score, 0.0);
479 }
480}