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;
22
23pub use dependency_parser::{
25 DependencyInfo, DependencyAnalysis, DetailedDependencyMap,
26 Vulnerability, VulnerabilitySeverity
27};
28
29pub use security_analyzer::{
31 SecurityAnalyzer, SecurityReport, SecurityFinding, SecuritySeverity,
32 SecurityCategory, ComplianceStatus, SecurityAnalysisConfig
33};
34
35#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
37pub struct DetectedLanguage {
38 pub name: String,
39 pub version: Option<String>,
40 pub confidence: f32,
41 pub files: Vec<PathBuf>,
42 pub main_dependencies: Vec<String>,
43 pub dev_dependencies: Vec<String>,
44 pub package_manager: Option<String>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
49pub enum TechnologyCategory {
50 MetaFramework,
52 FrontendFramework,
54 BackendFramework,
56 Library(LibraryType),
58 BuildTool,
60 Database,
62 Testing,
64 Runtime,
66 PackageManager,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
72pub enum LibraryType {
73 UI,
75 StateManagement,
77 DataFetching,
79 Routing,
81 Styling,
83 Utility,
85 HttpClient,
87 Authentication,
89 Other(String),
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
95pub struct DetectedTechnology {
96 pub name: String,
97 pub version: Option<String>,
98 pub category: TechnologyCategory,
99 pub confidence: f32,
100 pub requires: Vec<String>,
102 pub conflicts_with: Vec<String>,
104 pub is_primary: bool,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
110pub struct ServiceAnalysis {
111 pub name: String,
112 pub path: PathBuf,
113 pub languages: Vec<DetectedLanguage>,
114 pub technologies: Vec<DetectedTechnology>,
115 pub entry_points: Vec<EntryPoint>,
116 pub ports: Vec<Port>,
117 pub environment_variables: Vec<EnvVar>,
118 pub build_scripts: Vec<BuildScript>,
119 pub service_type: ProjectType,
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
124pub struct EntryPoint {
125 pub file: PathBuf,
126 pub function: Option<String>,
127 pub command: Option<String>,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
132pub struct Port {
133 pub number: u16,
134 pub protocol: Protocol,
135 pub description: Option<String>,
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
139pub enum Protocol {
140 Tcp,
141 Udp,
142 Http,
143 Https,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
148pub struct EnvVar {
149 pub name: String,
150 pub default_value: Option<String>,
151 pub required: bool,
152 pub description: Option<String>,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
157pub enum ProjectType {
158 WebApplication,
159 ApiService,
160 CliTool,
161 Library,
162 MobileApp,
163 DesktopApp,
164 Microservice,
165 StaticSite,
166 Hybrid, Unknown,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
172pub struct BuildScript {
173 pub name: String,
174 pub command: String,
175 pub description: Option<String>,
176 pub is_default: bool,
177}
178
179pub type DependencyMap = HashMap<String, String>;
181
182#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
184pub enum ArchitectureType {
185 Monolithic,
187 Microservices,
189 Hybrid,
191}
192
193pub type DetectedFramework = DetectedTechnology;
195
196#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
198pub struct ProjectAnalysis {
199 pub project_root: PathBuf,
200 pub languages: Vec<DetectedLanguage>,
201 pub technologies: Vec<DetectedTechnology>,
203 #[deprecated(note = "Use technologies field instead")]
205 pub frameworks: Vec<DetectedFramework>,
206 pub dependencies: DependencyMap,
207 pub entry_points: Vec<EntryPoint>,
208 pub ports: Vec<Port>,
209 pub environment_variables: Vec<EnvVar>,
210 pub project_type: ProjectType,
211 pub build_scripts: Vec<BuildScript>,
212 pub services: Vec<ServiceAnalysis>,
214 pub architecture_type: ArchitectureType,
216 pub analysis_metadata: AnalysisMetadata,
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
221pub struct AnalysisMetadata {
222 pub timestamp: String,
223 pub analyzer_version: String,
224 pub analysis_duration_ms: u64,
225 pub files_analyzed: usize,
226 pub confidence_score: f32,
227}
228
229#[derive(Debug, Clone)]
231pub struct AnalysisConfig {
232 pub include_dev_dependencies: bool,
233 pub deep_analysis: bool,
234 pub ignore_patterns: Vec<String>,
235 pub max_file_size: usize,
236}
237
238impl Default for AnalysisConfig {
239 fn default() -> Self {
240 Self {
241 include_dev_dependencies: false,
242 deep_analysis: true,
243 ignore_patterns: vec![
244 "node_modules".to_string(),
245 ".git".to_string(),
246 "target".to_string(),
247 "build".to_string(),
248 ".next".to_string(),
249 "dist".to_string(),
250 ],
251 max_file_size: 1024 * 1024, }
253 }
254}
255
256pub fn analyze_project(path: &Path) -> Result<ProjectAnalysis> {
276 analyze_project_with_config(path, &AnalysisConfig::default())
277}
278
279pub fn analyze_project_with_config(path: &Path, config: &AnalysisConfig) -> Result<ProjectAnalysis> {
281 let start_time = std::time::Instant::now();
282
283 let project_root = crate::common::file_utils::validate_project_path(path)?;
285
286 log::info!("Starting analysis of project: {}", project_root.display());
287
288 let files = crate::common::file_utils::collect_project_files(&project_root, config)?;
290 log::debug!("Found {} files to analyze", files.len());
291
292 let languages = language_detector::detect_languages(&files, config)?;
294 let frameworks = framework_detector::detect_frameworks(&project_root, &languages, config)?;
295 let dependencies = dependency_parser::parse_dependencies(&project_root, &languages, config)?;
296 let context = project_context::analyze_context(&project_root, &languages, &frameworks, config)?;
297
298 let duration = start_time.elapsed();
299 let confidence = calculate_confidence_score(&languages, &frameworks);
300
301 #[allow(deprecated)]
302 let analysis = ProjectAnalysis {
303 project_root,
304 languages,
305 technologies: frameworks.clone(), frameworks, dependencies,
308 entry_points: context.entry_points,
309 ports: context.ports,
310 environment_variables: context.environment_variables,
311 project_type: context.project_type,
312 build_scripts: context.build_scripts,
313 services: vec![], architecture_type: ArchitectureType::Monolithic, analysis_metadata: AnalysisMetadata {
316 timestamp: Utc::now().to_rfc3339(),
317 analyzer_version: env!("CARGO_PKG_VERSION").to_string(),
318 analysis_duration_ms: duration.as_millis() as u64,
319 files_analyzed: files.len(),
320 confidence_score: confidence,
321 },
322 };
323
324 log::info!("Analysis completed in {}ms", duration.as_millis());
325 Ok(analysis)
326}
327
328fn calculate_confidence_score(
330 languages: &[DetectedLanguage],
331 frameworks: &[DetectedFramework],
332) -> f32 {
333 if languages.is_empty() {
334 return 0.0;
335 }
336
337 let lang_confidence: f32 = languages.iter().map(|l| l.confidence).sum::<f32>() / languages.len() as f32;
338 let framework_confidence: f32 = if frameworks.is_empty() {
339 0.5 } else {
341 frameworks.iter().map(|f| f.confidence).sum::<f32>() / frameworks.len() as f32
342 };
343
344 (lang_confidence * 0.7 + framework_confidence * 0.3).min(1.0)
345}
346
347#[cfg(test)]
348mod tests {
349 use super::*;
350
351 #[test]
352 fn test_confidence_calculation() {
353 let languages = vec![
354 DetectedLanguage {
355 name: "Rust".to_string(),
356 version: Some("1.70.0".to_string()),
357 confidence: 0.9,
358 files: vec![],
359 main_dependencies: vec!["serde".to_string(), "tokio".to_string()],
360 dev_dependencies: vec!["assert_cmd".to_string()],
361 package_manager: Some("cargo".to_string()),
362 }
363 ];
364
365 let technologies = vec![
366 DetectedTechnology {
367 name: "Actix Web".to_string(),
368 version: Some("4.0".to_string()),
369 category: TechnologyCategory::BackendFramework,
370 confidence: 0.8,
371 requires: vec!["serde".to_string(), "tokio".to_string()],
372 conflicts_with: vec![],
373 is_primary: true,
374 }
375 ];
376
377 let frameworks = technologies.clone(); let score = calculate_confidence_score(&languages, &frameworks);
380 assert!(score > 0.8);
381 assert!(score <= 1.0);
382 }
383
384 #[test]
385 fn test_empty_analysis() {
386 let languages = vec![];
387 let frameworks = vec![];
388 let score = calculate_confidence_score(&languages, &frameworks);
389 assert_eq!(score, 0.0);
390 }
391}