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