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