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