syncable_cli/analyzer/
mod.rs

1//! # Analyzer Module
2//! 
3//! This module provides project analysis capabilities for detecting:
4//! - Programming languages and their versions
5//! - Frameworks and libraries
6//! - Dependencies and their versions
7//! - Entry points and exposed ports
8
9use 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 project_context;
20pub mod vulnerability_checker;
21pub mod security_analyzer;
22pub mod security;
23pub mod tool_installer;
24pub mod monorepo_detector;
25pub mod docker_analyzer;
26pub mod display;
27
28// Re-export dependency analysis types
29pub use dependency_parser::{
30    DependencyInfo, DependencyAnalysis, DetailedDependencyMap,
31    Vulnerability, VulnerabilitySeverity
32};
33
34// Re-export security analysis types
35pub use security_analyzer::{
36    SecurityAnalyzer, SecurityReport, SecurityFinding, SecuritySeverity,
37    SecurityCategory, ComplianceStatus, SecurityAnalysisConfig
38};
39
40// Re-export new modular security analysis types
41pub use security::{
42    ModularSecurityAnalyzer, JavaScriptSecurityAnalyzer, 
43    SecretPatternManager
44};
45pub use security::config::SecurityConfigPreset;
46
47// Re-export monorepo analysis types
48pub use monorepo_detector::{
49    MonorepoDetectionConfig, analyze_monorepo, analyze_monorepo_with_config
50};
51
52// Re-export Docker analysis types
53pub use docker_analyzer::{
54    DockerAnalysis, DockerfileInfo, ComposeFileInfo, DockerService, 
55    OrchestrationPattern, NetworkingConfig, DockerEnvironment,
56    analyze_docker_infrastructure
57};
58
59/// Represents a detected programming language
60#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
61pub struct DetectedLanguage {
62    pub name: String,
63    pub version: Option<String>,
64    pub confidence: f32,
65    pub files: Vec<PathBuf>,
66    pub main_dependencies: Vec<String>,
67    pub dev_dependencies: Vec<String>,
68    pub package_manager: Option<String>,
69}
70
71/// Categories of detected technologies with proper classification
72#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
73pub enum TechnologyCategory {
74    /// Full-stack meta-frameworks that provide complete application structure
75    MetaFramework,
76    /// Frontend frameworks that provide application structure (Angular, Svelte)
77    FrontendFramework,
78    /// Backend frameworks that provide server structure (Express, Django, Spring Boot)
79    BackendFramework,
80    /// Libraries that provide specific functionality (React, Tanstack Query, Axios)
81    Library(LibraryType),
82    /// Build and development tools (Vite, Webpack, Rollup)
83    BuildTool,
84    /// Database and ORM tools (Prisma, TypeORM, SQLAlchemy)
85    Database,
86    /// Testing frameworks and libraries (Jest, Vitest, Cypress)
87    Testing,
88    /// JavaScript/Python/etc runtimes (Node.js, Bun, Deno)
89    Runtime,
90    /// Package managers (npm, yarn, pnpm, pip, cargo)
91    PackageManager,
92}
93
94/// Specific types of libraries for better classification
95#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
96pub enum LibraryType {
97    /// UI libraries (React, Vue, Preact)
98    UI,
99    /// State management (Zustand, Redux, Pinia)
100    StateManagement,
101    /// Data fetching (Tanstack Query, Apollo, Relay)
102    DataFetching,
103    /// Routing (React Router, Vue Router - when not meta-framework)
104    Routing,
105    /// Styling (Styled Components, Emotion, Tailwind)
106    Styling,
107    /// Utilities (Lodash, Date-fns, Zod)
108    Utility,
109    /// HTTP clients (Axios, Fetch libraries)
110    HttpClient,
111    /// Authentication (Auth0, Firebase Auth)
112    Authentication,
113    /// CLI frameworks (clap, structopt, argh)
114    CLI,
115    /// Other specific types
116    Other(String),
117}
118
119/// Represents a detected technology (framework, library, or tool)
120#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
121pub struct DetectedTechnology {
122    pub name: String,
123    pub version: Option<String>,
124    pub category: TechnologyCategory,
125    pub confidence: f32,
126    /// Dependencies this technology requires (e.g., Next.js requires React)
127    pub requires: Vec<String>,
128    /// Technologies that conflict with this one (e.g., Tanstack Start conflicts with React Router v7)
129    pub conflicts_with: Vec<String>,
130    /// Whether this is the primary technology driving the architecture
131    pub is_primary: bool,
132}
133
134/// Represents a service within a microservice architecture
135#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
136pub struct ServiceAnalysis {
137    pub name: String,
138    pub path: PathBuf,
139    pub languages: Vec<DetectedLanguage>,
140    pub technologies: Vec<DetectedTechnology>,
141    pub entry_points: Vec<EntryPoint>,
142    pub ports: Vec<Port>,
143    pub environment_variables: Vec<EnvVar>,
144    pub build_scripts: Vec<BuildScript>,
145    pub service_type: ProjectType,
146}
147
148/// Represents application entry points
149#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
150pub struct EntryPoint {
151    pub file: PathBuf,
152    pub function: Option<String>,
153    pub command: Option<String>,
154}
155
156/// Represents exposed network ports
157#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
158pub struct Port {
159    pub number: u16,
160    pub protocol: Protocol,
161    pub description: Option<String>,
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
165pub enum Protocol {
166    Tcp,
167    Udp,
168    Http,
169    Https,
170}
171
172/// Represents environment variables
173#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
174pub struct EnvVar {
175    pub name: String,
176    pub default_value: Option<String>,
177    pub required: bool,
178    pub description: Option<String>,
179}
180
181/// Represents different project types
182#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
183pub enum ProjectType {
184    WebApplication,
185    ApiService,
186    CliTool,
187    Library,
188    MobileApp,
189    DesktopApp,
190    Microservice,
191    StaticSite,
192    Hybrid, // Multiple types
193    Unknown,
194}
195
196/// Represents build scripts and commands
197#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
198pub struct BuildScript {
199    pub name: String,
200    pub command: String,
201    pub description: Option<String>,
202    pub is_default: bool,
203}
204
205/// Type alias for dependency maps
206pub type DependencyMap = HashMap<String, String>;
207
208/// Types of project architectures
209#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
210pub enum ArchitectureType {
211    /// Single application/service
212    Monolithic,
213    /// Multiple services in one repository
214    Microservices,
215    /// Mixed approach with both
216    Hybrid,
217}
218
219/// Backward compatibility type alias
220pub type DetectedFramework = DetectedTechnology;
221
222/// Enhanced project analysis with proper technology classification and microservice support
223#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
224pub struct ProjectAnalysis {
225    pub project_root: PathBuf,
226    pub languages: Vec<DetectedLanguage>,
227    /// All detected technologies (frameworks, libraries, tools) with proper classification
228    pub technologies: Vec<DetectedTechnology>,
229    /// Legacy field for backward compatibility - will be populated from technologies
230    #[deprecated(note = "Use technologies field instead")]
231    pub frameworks: Vec<DetectedFramework>,
232    pub dependencies: DependencyMap,
233    pub entry_points: Vec<EntryPoint>,
234    pub ports: Vec<Port>,
235    pub environment_variables: Vec<EnvVar>,
236    pub project_type: ProjectType,
237    pub build_scripts: Vec<BuildScript>,
238    /// Individual service analyses for microservice architectures
239    pub services: Vec<ServiceAnalysis>,
240    /// Whether this is a monolithic project or microservice architecture
241    pub architecture_type: ArchitectureType,
242    /// Docker infrastructure analysis
243    pub docker_analysis: Option<DockerAnalysis>,
244    pub analysis_metadata: AnalysisMetadata,
245}
246
247/// Metadata about the analysis process
248#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
249pub struct AnalysisMetadata {
250    pub timestamp: String,
251    pub analyzer_version: String,
252    pub analysis_duration_ms: u64,
253    pub files_analyzed: usize,
254    pub confidence_score: f32,
255}
256
257/// Configuration for project analysis
258#[derive(Debug, Clone)]
259pub struct AnalysisConfig {
260    pub include_dev_dependencies: bool,
261    pub deep_analysis: bool,
262    pub ignore_patterns: Vec<String>,
263    pub max_file_size: usize,
264}
265
266impl Default for AnalysisConfig {
267    fn default() -> Self {
268        Self {
269            include_dev_dependencies: false,
270            deep_analysis: true,
271            ignore_patterns: vec![
272                "node_modules".to_string(),
273                ".git".to_string(),
274                "target".to_string(),
275                "build".to_string(),
276                ".next".to_string(),
277                "dist".to_string(),
278            ],
279            max_file_size: 1024 * 1024, // 1MB
280        }
281    }
282}
283
284/// Represents an individual project within a monorepo
285#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
286pub struct ProjectInfo {
287    /// Relative path from the monorepo root
288    pub path: PathBuf,
289    /// Display name for the project (derived from directory name or package name)
290    pub name: String,
291    /// Type of project (frontend, backend, service, etc.)
292    pub project_category: ProjectCategory,
293    /// Full analysis of this specific project
294    pub analysis: ProjectAnalysis,
295}
296
297/// Category of project within a monorepo
298#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
299pub enum ProjectCategory {
300    Frontend,
301    Backend,
302    Api,
303    Service,
304    Library,
305    Tool,
306    Documentation,
307    Infrastructure,
308    Unknown,
309}
310
311/// Represents the overall analysis of a monorepo or single project
312#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
313pub struct MonorepoAnalysis {
314    /// Root path of the analysis
315    pub root_path: PathBuf,
316    /// Whether this is a monorepo (multiple projects) or single project
317    pub is_monorepo: bool,
318    /// List of detected projects (will have 1 item for single projects)
319    pub projects: Vec<ProjectInfo>,
320    /// Overall metadata for the entire analysis
321    pub metadata: AnalysisMetadata,
322    /// Summary of all technologies found across projects
323    pub technology_summary: TechnologySummary,
324}
325
326/// Summary of technologies across all projects
327#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
328pub struct TechnologySummary {
329    pub languages: Vec<String>,
330    pub frameworks: Vec<String>,
331    pub databases: Vec<String>,
332    pub total_projects: usize,
333    pub architecture_pattern: ArchitecturePattern,
334}
335
336/// Detected architecture patterns
337#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
338pub enum ArchitecturePattern {
339    /// Single application
340    Monolithic,
341    /// Frontend + Backend separation
342    Fullstack,
343    /// Multiple independent services
344    Microservices,
345    /// API-first architecture
346    ApiFirst,
347    /// Event-driven architecture
348    EventDriven,
349    /// Unknown or mixed pattern
350    Mixed,
351}
352
353/// Analyzes a project directory to detect languages, frameworks, and dependencies.
354/// 
355/// # Arguments
356/// * `path` - The root directory of the project to analyze
357/// 
358/// # Returns
359/// A `ProjectAnalysis` containing detected components or an error
360/// 
361/// # Examples
362/// ```no_run
363/// use syncable_cli::analyzer::analyze_project;
364/// use std::path::Path;
365/// 
366/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
367/// let analysis = analyze_project(Path::new("./my-project"))?;
368/// println!("Languages: {:?}", analysis.languages);
369/// # Ok(())
370/// # }
371/// ```
372pub fn analyze_project(path: &Path) -> Result<ProjectAnalysis> {
373    analyze_project_with_config(path, &AnalysisConfig::default())
374}
375
376/// Analyzes a project with custom configuration
377pub fn analyze_project_with_config(path: &Path, config: &AnalysisConfig) -> Result<ProjectAnalysis> {
378    let start_time = std::time::Instant::now();
379    
380    // Validate project path
381    let project_root = crate::common::file_utils::validate_project_path(path)?;
382    
383    log::info!("Starting analysis of project: {}", project_root.display());
384    
385    // Collect project files
386    let files = crate::common::file_utils::collect_project_files(&project_root, config)?;
387    log::debug!("Found {} files to analyze", files.len());
388    
389    // Perform parallel analysis
390    let languages = language_detector::detect_languages(&files, config)?;
391    let frameworks = framework_detector::detect_frameworks(&project_root, &languages, config)?;
392    let dependencies = dependency_parser::parse_dependencies(&project_root, &languages, config)?;
393    let context = project_context::analyze_context(&project_root, &languages, &frameworks, config)?;
394    
395    // Analyze Docker infrastructure
396    let docker_analysis = analyze_docker_infrastructure(&project_root).ok();
397    
398    let duration = start_time.elapsed();
399    let confidence = calculate_confidence_score(&languages, &frameworks);
400    
401    #[allow(deprecated)]
402    let analysis = ProjectAnalysis {
403        project_root,
404        languages,
405        technologies: frameworks.clone(), // New field with proper technology classification
406        frameworks, // Backward compatibility
407        dependencies,
408        entry_points: context.entry_points,
409        ports: context.ports,
410        environment_variables: context.environment_variables,
411        project_type: context.project_type,
412        build_scripts: context.build_scripts,
413        services: vec![], // TODO: Implement microservice detection
414        architecture_type: ArchitectureType::Monolithic, // TODO: Detect architecture type
415        docker_analysis,
416        analysis_metadata: AnalysisMetadata {
417            timestamp: Utc::now().to_rfc3339(),
418            analyzer_version: env!("CARGO_PKG_VERSION").to_string(),
419            analysis_duration_ms: duration.as_millis() as u64,
420            files_analyzed: files.len(),
421            confidence_score: confidence,
422        },
423    };
424    
425    log::info!("Analysis completed in {}ms", duration.as_millis());
426    Ok(analysis)
427}
428
429/// Calculate overall confidence score based on detection results
430fn calculate_confidence_score(
431    languages: &[DetectedLanguage],
432    frameworks: &[DetectedFramework],
433) -> f32 {
434    if languages.is_empty() {
435        return 0.0;
436    }
437    
438    let lang_confidence: f32 = languages.iter().map(|l| l.confidence).sum::<f32>() / languages.len() as f32;
439    let framework_confidence: f32 = if frameworks.is_empty() {
440        0.5 // Neutral score if no frameworks detected
441    } else {
442        frameworks.iter().map(|f| f.confidence).sum::<f32>() / frameworks.len() as f32
443    };
444    
445    (lang_confidence * 0.7 + framework_confidence * 0.3).min(1.0)
446}
447
448#[cfg(test)]
449mod tests {
450    use super::*;
451    
452    #[test]
453    fn test_confidence_calculation() {
454        let languages = vec![
455            DetectedLanguage {
456                name: "Rust".to_string(),
457                version: Some("1.70.0".to_string()),
458                confidence: 0.9,
459                files: vec![],
460                main_dependencies: vec!["serde".to_string(), "tokio".to_string()],
461                dev_dependencies: vec!["assert_cmd".to_string()],
462                package_manager: Some("cargo".to_string()),
463            }
464        ];
465        
466        let technologies = vec![
467            DetectedTechnology {
468                name: "Actix Web".to_string(),
469                version: Some("4.0".to_string()),
470                category: TechnologyCategory::BackendFramework,
471                confidence: 0.8,
472                requires: vec!["serde".to_string(), "tokio".to_string()],
473                conflicts_with: vec![],
474                is_primary: true,
475            }
476        ];
477        
478        let frameworks = technologies.clone(); // For backward compatibility
479        
480        let score = calculate_confidence_score(&languages, &frameworks);
481        assert!(score > 0.8);
482        assert!(score <= 1.0);
483    }
484    
485    #[test]
486    fn test_empty_analysis() {
487        let languages = vec![];
488        let frameworks = vec![];
489        let score = calculate_confidence_score(&languages, &frameworks);
490        assert_eq!(score, 0.0);
491    }
492}