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 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
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 security analysis types
41pub use security::{
42    SecretPatternManager
43};
44pub use security::config::SecurityConfigPreset;
45
46// Re-export monorepo analysis types
47pub use monorepo::{
48    MonorepoDetectionConfig, analyze_monorepo, analyze_monorepo_with_config
49};
50
51// Re-export Docker analysis types
52pub use docker_analyzer::{
53    DockerAnalysis, DockerfileInfo, ComposeFileInfo, DockerService, 
54    OrchestrationPattern, NetworkingConfig, DockerEnvironment,
55    analyze_docker_infrastructure
56};
57
58/// Represents a detected programming language
59#[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/// Categories of detected technologies with proper classification
71#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
72pub enum TechnologyCategory {
73    /// Full-stack meta-frameworks that provide complete application structure
74    MetaFramework,
75    /// Frontend frameworks that provide application structure (Angular, Svelte)
76    FrontendFramework,
77    /// Backend frameworks that provide server structure (Express, Django, Spring Boot)
78    BackendFramework,
79    /// Libraries that provide specific functionality (React, Tanstack Query, Axios)
80    Library(LibraryType),
81    /// Build and development tools (Vite, Webpack, Rollup)
82    BuildTool,
83    /// Database and ORM tools (Prisma, TypeORM, SQLAlchemy)
84    Database,
85    /// Testing frameworks and libraries (Jest, Vitest, Cypress)
86    Testing,
87    /// JavaScript/Python/etc runtimes (Node.js, Bun, Deno)
88    Runtime,
89    /// Package managers (npm, yarn, pnpm, pip, cargo)
90    PackageManager,
91}
92
93/// Specific types of libraries for better classification
94#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
95pub enum LibraryType {
96    /// UI libraries (React, Vue, Preact)
97    UI,
98    /// State management (Zustand, Redux, Pinia)
99    StateManagement,
100    /// Data fetching (Tanstack Query, Apollo, Relay)
101    DataFetching,
102    /// Routing (React Router, Vue Router - when not meta-framework)
103    Routing,
104    /// Styling (Styled Components, Emotion, Tailwind)
105    Styling,
106    /// Utilities (Lodash, Date-fns, Zod)
107    Utility,
108    /// HTTP clients (Axios, Fetch libraries)
109    HttpClient,
110    /// Authentication (Auth0, Firebase Auth)
111    Authentication,
112    /// CLI frameworks (clap, structopt, argh)
113    CLI,
114    /// Other specific types
115    Other(String),
116}
117
118/// Represents a detected technology (framework, library, or tool)
119#[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    /// Dependencies this technology requires (e.g., Next.js requires React)
126    pub requires: Vec<String>,
127    /// Technologies that conflict with this one (e.g., Tanstack Start conflicts with React Router v7)
128    pub conflicts_with: Vec<String>,
129    /// Whether this is the primary technology driving the architecture
130    pub is_primary: bool,
131}
132
133/// Represents a service within a microservice architecture
134#[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/// Represents application entry points
148#[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/// Represents exposed network ports
156#[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/// Represents environment variables
172#[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/// Represents different project types
181#[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, // Multiple types
192    Unknown,
193}
194
195/// Represents build scripts and commands
196#[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
204/// Type alias for dependency maps
205pub type DependencyMap = HashMap<String, String>;
206
207/// Types of project architectures
208#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
209pub enum ArchitectureType {
210    /// Single application/service
211    Monolithic,
212    /// Multiple services in one repository
213    Microservices,
214    /// Mixed approach with both
215    Hybrid,
216}
217
218/// Backward compatibility type alias
219pub type DetectedFramework = DetectedTechnology;
220
221/// Enhanced project analysis with proper technology classification and microservice support
222#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
223pub struct ProjectAnalysis {
224    pub project_root: PathBuf,
225    pub languages: Vec<DetectedLanguage>,
226    /// All detected technologies (frameworks, libraries, tools) with proper classification
227    pub technologies: Vec<DetectedTechnology>,
228    /// Legacy field for backward compatibility - will be populated from technologies
229    #[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    /// Individual service analyses for microservice architectures
238    pub services: Vec<ServiceAnalysis>,
239    /// Whether this is a monolithic project or microservice architecture
240    pub architecture_type: ArchitectureType,
241    /// Docker infrastructure analysis
242    pub docker_analysis: Option<DockerAnalysis>,
243    pub analysis_metadata: AnalysisMetadata,
244}
245
246/// Metadata about the analysis process
247#[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/// Configuration for project analysis
257#[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, // 1MB
279        }
280    }
281}
282
283/// Represents an individual project within a monorepo
284#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
285pub struct ProjectInfo {
286    /// Relative path from the monorepo root
287    pub path: PathBuf,
288    /// Display name for the project (derived from directory name or package name)
289    pub name: String,
290    /// Type of project (frontend, backend, service, etc.)
291    pub project_category: ProjectCategory,
292    /// Full analysis of this specific project
293    pub analysis: ProjectAnalysis,
294}
295
296/// Category of project within a monorepo
297#[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/// Represents the overall analysis of a monorepo or single project
311#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
312pub struct MonorepoAnalysis {
313    /// Root path of the analysis
314    pub root_path: PathBuf,
315    /// Whether this is a monorepo (multiple projects) or single project
316    pub is_monorepo: bool,
317    /// List of detected projects (will have 1 item for single projects)
318    pub projects: Vec<ProjectInfo>,
319    /// Overall metadata for the entire analysis
320    pub metadata: AnalysisMetadata,
321    /// Summary of all technologies found across projects
322    pub technology_summary: TechnologySummary,
323}
324
325/// Summary of technologies across all projects
326#[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/// Detected architecture patterns
336#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
337pub enum ArchitecturePattern {
338    /// Single application
339    Monolithic,
340    /// Frontend + Backend separation
341    Fullstack,
342    /// Multiple independent services
343    Microservices,
344    /// API-first architecture
345    ApiFirst,
346    /// Event-driven architecture
347    EventDriven,
348    /// Unknown or mixed pattern
349    Mixed,
350}
351
352/// Analyzes a project directory to detect languages, frameworks, and dependencies.
353/// 
354/// # Arguments
355/// * `path` - The root directory of the project to analyze
356/// 
357/// # Returns
358/// A `ProjectAnalysis` containing detected components or an error
359/// 
360/// # Examples
361/// ```no_run
362/// use syncable_cli::analyzer::analyze_project;
363/// use std::path::Path;
364/// 
365/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
366/// let analysis = analyze_project(Path::new("./my-project"))?;
367/// println!("Languages: {:?}", analysis.languages);
368/// # Ok(())
369/// # }
370/// ```
371pub fn analyze_project(path: &Path) -> Result<ProjectAnalysis> {
372    analyze_project_with_config(path, &AnalysisConfig::default())
373}
374
375/// Analyzes a project with custom configuration
376pub fn analyze_project_with_config(path: &Path, config: &AnalysisConfig) -> Result<ProjectAnalysis> {
377    let start_time = std::time::Instant::now();
378    
379    // Validate project path
380    let project_root = crate::common::file_utils::validate_project_path(path)?;
381    
382    log::info!("Starting analysis of project: {}", project_root.display());
383    
384    // Collect project files
385    let files = crate::common::file_utils::collect_project_files(&project_root, config)?;
386    log::debug!("Found {} files to analyze", files.len());
387    
388    // Perform parallel analysis
389    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    // Analyze Docker infrastructure
395    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(), // New field with proper technology classification
405        frameworks, // Backward compatibility
406        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![], // TODO: Implement microservice detection
413        architecture_type: ArchitectureType::Monolithic, // TODO: Detect architecture type
414        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
428/// Calculate overall confidence score based on detection results
429fn 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 // Neutral score if no frameworks detected
440    } 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(); // For backward compatibility
478        
479        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}