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 frameworks;
18pub mod language_detector;
19pub mod context;
20pub mod vulnerability_checker;
21pub mod security_analyzer;
22pub mod security;
23pub mod tool_installer;
24pub mod monorepo;
25pub mod docker_analyzer;
26pub mod display;
27
28pub use dependency_parser::{
30    DependencyInfo, DependencyAnalysis, DetailedDependencyMap,
31    Vulnerability, VulnerabilitySeverity
32};
33
34pub use security_analyzer::{
36    SecurityAnalyzer, SecurityReport, SecurityFinding, SecuritySeverity,
37    SecurityCategory, ComplianceStatus, SecurityAnalysisConfig
38};
39
40pub use security::{
42    SecretPatternManager
43};
44pub use security::config::SecurityConfigPreset;
45
46pub use monorepo::{
48    MonorepoDetectionConfig, analyze_monorepo, analyze_monorepo_with_config
49};
50
51pub use docker_analyzer::{
53    DockerAnalysis, DockerfileInfo, ComposeFileInfo, DockerService, 
54    OrchestrationPattern, NetworkingConfig, DockerEnvironment,
55    analyze_docker_infrastructure
56};
57
58#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
60pub struct DetectedLanguage {
61    pub name: String,
62    pub version: Option<String>,
63    pub confidence: f32,
64    pub files: Vec<PathBuf>,
65    pub main_dependencies: Vec<String>,
66    pub dev_dependencies: Vec<String>,
67    pub package_manager: Option<String>,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
72pub enum TechnologyCategory {
73    MetaFramework,
75    FrontendFramework,
77    BackendFramework,
79    Library(LibraryType),
81    BuildTool,
83    Database,
85    Testing,
87    Runtime,
89    PackageManager,
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
95pub enum LibraryType {
96    UI,
98    StateManagement,
100    DataFetching,
102    Routing,
104    Styling,
106    Utility,
108    HttpClient,
110    Authentication,
112    CLI,
114    Other(String),
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
120pub struct DetectedTechnology {
121    pub name: String,
122    pub version: Option<String>,
123    pub category: TechnologyCategory,
124    pub confidence: f32,
125    pub requires: Vec<String>,
127    pub conflicts_with: Vec<String>,
129    pub is_primary: bool,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
135pub struct ServiceAnalysis {
136    pub name: String,
137    pub path: PathBuf,
138    pub languages: Vec<DetectedLanguage>,
139    pub technologies: Vec<DetectedTechnology>,
140    pub entry_points: Vec<EntryPoint>,
141    pub ports: Vec<Port>,
142    pub environment_variables: Vec<EnvVar>,
143    pub build_scripts: Vec<BuildScript>,
144    pub service_type: ProjectType,
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
149pub struct EntryPoint {
150    pub file: PathBuf,
151    pub function: Option<String>,
152    pub command: Option<String>,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
157pub struct Port {
158    pub number: u16,
159    pub protocol: Protocol,
160    pub description: Option<String>,
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
164pub enum Protocol {
165    Tcp,
166    Udp,
167    Http,
168    Https,
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
173pub struct EnvVar {
174    pub name: String,
175    pub default_value: Option<String>,
176    pub required: bool,
177    pub description: Option<String>,
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
182pub enum ProjectType {
183    WebApplication,
184    ApiService,
185    CliTool,
186    Library,
187    MobileApp,
188    DesktopApp,
189    Microservice,
190    StaticSite,
191    Hybrid, Unknown,
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
197pub struct BuildScript {
198    pub name: String,
199    pub command: String,
200    pub description: Option<String>,
201    pub is_default: bool,
202}
203
204pub type DependencyMap = HashMap<String, String>;
206
207#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
209pub enum ArchitectureType {
210    Monolithic,
212    Microservices,
214    Hybrid,
216}
217
218pub type DetectedFramework = DetectedTechnology;
220
221#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
223pub struct ProjectAnalysis {
224    pub project_root: PathBuf,
225    pub languages: Vec<DetectedLanguage>,
226    pub technologies: Vec<DetectedTechnology>,
228    #[deprecated(note = "Use technologies field instead")]
230    pub frameworks: Vec<DetectedFramework>,
231    pub dependencies: DependencyMap,
232    pub entry_points: Vec<EntryPoint>,
233    pub ports: Vec<Port>,
234    pub environment_variables: Vec<EnvVar>,
235    pub project_type: ProjectType,
236    pub build_scripts: Vec<BuildScript>,
237    pub services: Vec<ServiceAnalysis>,
239    pub architecture_type: ArchitectureType,
241    pub docker_analysis: Option<DockerAnalysis>,
243    pub analysis_metadata: AnalysisMetadata,
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
248pub struct AnalysisMetadata {
249    pub timestamp: String,
250    pub analyzer_version: String,
251    pub analysis_duration_ms: u64,
252    pub files_analyzed: usize,
253    pub confidence_score: f32,
254}
255
256#[derive(Debug, Clone)]
258pub struct AnalysisConfig {
259    pub include_dev_dependencies: bool,
260    pub deep_analysis: bool,
261    pub ignore_patterns: Vec<String>,
262    pub max_file_size: usize,
263}
264
265impl Default for AnalysisConfig {
266    fn default() -> Self {
267        Self {
268            include_dev_dependencies: false,
269            deep_analysis: true,
270            ignore_patterns: vec![
271                "node_modules".to_string(),
272                ".git".to_string(),
273                "target".to_string(),
274                "build".to_string(),
275                ".next".to_string(),
276                "dist".to_string(),
277            ],
278            max_file_size: 1024 * 1024, }
280    }
281}
282
283#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
285pub struct ProjectInfo {
286    pub path: PathBuf,
288    pub name: String,
290    pub project_category: ProjectCategory,
292    pub analysis: ProjectAnalysis,
294}
295
296#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
298pub enum ProjectCategory {
299    Frontend,
300    Backend,
301    Api,
302    Service,
303    Library,
304    Tool,
305    Documentation,
306    Infrastructure,
307    Unknown,
308}
309
310#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
312pub struct MonorepoAnalysis {
313    pub root_path: PathBuf,
315    pub is_monorepo: bool,
317    pub projects: Vec<ProjectInfo>,
319    pub metadata: AnalysisMetadata,
321    pub technology_summary: TechnologySummary,
323}
324
325#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
327pub struct TechnologySummary {
328    pub languages: Vec<String>,
329    pub frameworks: Vec<String>,
330    pub databases: Vec<String>,
331    pub total_projects: usize,
332    pub architecture_pattern: ArchitecturePattern,
333}
334
335#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
337pub enum ArchitecturePattern {
338    Monolithic,
340    Fullstack,
342    Microservices,
344    ApiFirst,
346    EventDriven,
348    Mixed,
350}
351
352pub fn analyze_project(path: &Path) -> Result<ProjectAnalysis> {
372    analyze_project_with_config(path, &AnalysisConfig::default())
373}
374
375pub fn analyze_project_with_config(path: &Path, config: &AnalysisConfig) -> Result<ProjectAnalysis> {
377    let start_time = std::time::Instant::now();
378    
379    let project_root = crate::common::file_utils::validate_project_path(path)?;
381    
382    log::info!("Starting analysis of project: {}", project_root.display());
383    
384    let files = crate::common::file_utils::collect_project_files(&project_root, config)?;
386    log::debug!("Found {} files to analyze", files.len());
387    
388    let languages = language_detector::detect_languages(&files, config)?;
390    let frameworks = framework_detector::detect_frameworks(&project_root, &languages, config)?;
391    let dependencies = dependency_parser::parse_dependencies(&project_root, &languages, config)?;
392    let context = context::analyze_context(&project_root, &languages, &frameworks, config)?;
393    
394    let docker_analysis = analyze_docker_infrastructure(&project_root).ok();
396    
397    let duration = start_time.elapsed();
398    let confidence = calculate_confidence_score(&languages, &frameworks);
399    
400    #[allow(deprecated)]
401    let analysis = ProjectAnalysis {
402        project_root,
403        languages,
404        technologies: frameworks.clone(), frameworks, dependencies,
407        entry_points: context.entry_points,
408        ports: context.ports,
409        environment_variables: context.environment_variables,
410        project_type: context.project_type,
411        build_scripts: context.build_scripts,
412        services: vec![], architecture_type: ArchitectureType::Monolithic, docker_analysis,
415        analysis_metadata: AnalysisMetadata {
416            timestamp: Utc::now().to_rfc3339(),
417            analyzer_version: env!("CARGO_PKG_VERSION").to_string(),
418            analysis_duration_ms: duration.as_millis() as u64,
419            files_analyzed: files.len(),
420            confidence_score: confidence,
421        },
422    };
423    
424    log::info!("Analysis completed in {}ms", duration.as_millis());
425    Ok(analysis)
426}
427
428fn calculate_confidence_score(
430    languages: &[DetectedLanguage],
431    frameworks: &[DetectedFramework],
432) -> f32 {
433    if languages.is_empty() {
434        return 0.0;
435    }
436    
437    let lang_confidence: f32 = languages.iter().map(|l| l.confidence).sum::<f32>() / languages.len() as f32;
438    let framework_confidence: f32 = if frameworks.is_empty() {
439        0.5 } else {
441        frameworks.iter().map(|f| f.confidence).sum::<f32>() / frameworks.len() as f32
442    };
443    
444    (lang_confidence * 0.7 + framework_confidence * 0.3).min(1.0)
445}
446
447#[cfg(test)]
448mod tests {
449    use super::*;
450    
451    #[test]
452    fn test_confidence_calculation() {
453        let languages = vec![
454            DetectedLanguage {
455                name: "Rust".to_string(),
456                version: Some("1.70.0".to_string()),
457                confidence: 0.9,
458                files: vec![],
459                main_dependencies: vec!["serde".to_string(), "tokio".to_string()],
460                dev_dependencies: vec!["assert_cmd".to_string()],
461                package_manager: Some("cargo".to_string()),
462            }
463        ];
464        
465        let technologies = vec![
466            DetectedTechnology {
467                name: "Actix Web".to_string(),
468                version: Some("4.0".to_string()),
469                category: TechnologyCategory::BackendFramework,
470                confidence: 0.8,
471                requires: vec!["serde".to_string(), "tokio".to_string()],
472                conflicts_with: vec![],
473                is_primary: true,
474            }
475        ];
476        
477        let frameworks = technologies.clone(); let score = calculate_confidence_score(&languages, &frameworks);
480        assert!(score > 0.8);
481        assert!(score <= 1.0);
482    }
483    
484    #[test]
485    fn test_empty_analysis() {
486        let languages = vec![];
487        let frameworks = vec![];
488        let score = calculate_confidence_score(&languages, &frameworks);
489        assert_eq!(score, 0.0);
490    }
491}