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}
142
143/// Represents a service within a microservice architecture
144#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
145pub struct ServiceAnalysis {
146    pub name: String,
147    pub path: PathBuf,
148    pub languages: Vec<DetectedLanguage>,
149    pub technologies: Vec<DetectedTechnology>,
150    pub entry_points: Vec<EntryPoint>,
151    pub ports: Vec<Port>,
152    pub environment_variables: Vec<EnvVar>,
153    pub build_scripts: Vec<BuildScript>,
154    pub service_type: ProjectType,
155}
156
157/// Represents application entry points
158#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
159pub struct EntryPoint {
160    pub file: PathBuf,
161    pub function: Option<String>,
162    pub command: Option<String>,
163}
164
165/// Represents exposed network ports
166#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
167pub struct Port {
168    pub number: u16,
169    pub protocol: Protocol,
170    pub description: Option<String>,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
174pub enum Protocol {
175    Tcp,
176    Udp,
177    Http,
178    Https,
179}
180
181/// Represents environment variables
182#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
183pub struct EnvVar {
184    pub name: String,
185    pub default_value: Option<String>,
186    pub required: bool,
187    pub description: Option<String>,
188}
189
190/// Represents different project types
191#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
192pub enum ProjectType {
193    WebApplication,
194    ApiService,
195    CliTool,
196    Library,
197    MobileApp,
198    DesktopApp,
199    Microservice,
200    StaticSite,
201    Hybrid, // Multiple types
202    Unknown,
203}
204
205/// Represents build scripts and commands
206#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
207pub struct BuildScript {
208    pub name: String,
209    pub command: String,
210    pub description: Option<String>,
211    pub is_default: bool,
212}
213
214/// Type alias for dependency maps
215pub type DependencyMap = HashMap<String, String>;
216
217/// Types of project architectures
218#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
219pub enum ArchitectureType {
220    /// Single application/service
221    Monolithic,
222    /// Multiple services in one repository
223    Microservices,
224    /// Mixed approach with both
225    Hybrid,
226}
227
228/// Backward compatibility type alias
229pub type DetectedFramework = DetectedTechnology;
230
231/// Enhanced project analysis with proper technology classification and microservice support
232#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
233pub struct ProjectAnalysis {
234    pub project_root: PathBuf,
235    pub languages: Vec<DetectedLanguage>,
236    /// All detected technologies (frameworks, libraries, tools) with proper classification
237    pub technologies: Vec<DetectedTechnology>,
238    /// Legacy field for backward compatibility - will be populated from technologies
239    #[deprecated(note = "Use technologies field instead")]
240    pub frameworks: Vec<DetectedFramework>,
241    pub dependencies: DependencyMap,
242    pub entry_points: Vec<EntryPoint>,
243    pub ports: Vec<Port>,
244    pub environment_variables: Vec<EnvVar>,
245    pub project_type: ProjectType,
246    pub build_scripts: Vec<BuildScript>,
247    /// Individual service analyses for microservice architectures
248    pub services: Vec<ServiceAnalysis>,
249    /// Whether this is a monolithic project or microservice architecture
250    pub architecture_type: ArchitectureType,
251    /// Docker infrastructure analysis
252    pub docker_analysis: Option<DockerAnalysis>,
253    pub analysis_metadata: AnalysisMetadata,
254}
255
256/// Metadata about the analysis process
257#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
258pub struct AnalysisMetadata {
259    pub timestamp: String,
260    pub analyzer_version: String,
261    pub analysis_duration_ms: u64,
262    pub files_analyzed: usize,
263    pub confidence_score: f32,
264}
265
266/// Configuration for project analysis
267#[derive(Debug, Clone)]
268pub struct AnalysisConfig {
269    pub include_dev_dependencies: bool,
270    pub deep_analysis: bool,
271    pub ignore_patterns: Vec<String>,
272    pub max_file_size: usize,
273}
274
275impl Default for AnalysisConfig {
276    fn default() -> Self {
277        Self {
278            include_dev_dependencies: false,
279            deep_analysis: true,
280            ignore_patterns: vec![
281                "node_modules".to_string(),
282                ".git".to_string(),
283                "target".to_string(),
284                "build".to_string(),
285                ".next".to_string(),
286                "dist".to_string(),
287            ],
288            max_file_size: 1024 * 1024, // 1MB
289        }
290    }
291}
292
293/// Represents an individual project within a monorepo
294#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
295pub struct ProjectInfo {
296    /// Relative path from the monorepo root
297    pub path: PathBuf,
298    /// Display name for the project (derived from directory name or package name)
299    pub name: String,
300    /// Type of project (frontend, backend, service, etc.)
301    pub project_category: ProjectCategory,
302    /// Full analysis of this specific project
303    pub analysis: ProjectAnalysis,
304}
305
306/// Category of project within a monorepo
307#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
308pub enum ProjectCategory {
309    Frontend,
310    Backend,
311    Api,
312    Service,
313    Library,
314    Tool,
315    Documentation,
316    Infrastructure,
317    Unknown,
318}
319
320/// Represents the overall analysis of a monorepo or single project
321#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
322pub struct MonorepoAnalysis {
323    /// Root path of the analysis
324    pub root_path: PathBuf,
325    /// Whether this is a monorepo (multiple projects) or single project
326    pub is_monorepo: bool,
327    /// List of detected projects (will have 1 item for single projects)
328    pub projects: Vec<ProjectInfo>,
329    /// Overall metadata for the entire analysis
330    pub metadata: AnalysisMetadata,
331    /// Summary of all technologies found across projects
332    pub technology_summary: TechnologySummary,
333}
334
335/// Summary of technologies across all projects
336#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
337pub struct TechnologySummary {
338    pub languages: Vec<String>,
339    pub frameworks: Vec<String>,
340    pub databases: Vec<String>,
341    pub total_projects: usize,
342    pub architecture_pattern: ArchitecturePattern,
343}
344
345/// Detected architecture patterns
346#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
347pub enum ArchitecturePattern {
348    /// Single application
349    Monolithic,
350    /// Frontend + Backend separation
351    Fullstack,
352    /// Multiple independent services
353    Microservices,
354    /// API-first architecture
355    ApiFirst,
356    /// Event-driven architecture
357    EventDriven,
358    /// Unknown or mixed pattern
359    Mixed,
360}
361
362/// Analyzes a project directory to detect languages, frameworks, and dependencies.
363/// 
364/// # Arguments
365/// * `path` - The root directory of the project to analyze
366/// 
367/// # Returns
368/// A `ProjectAnalysis` containing detected components or an error
369/// 
370/// # Examples
371/// ```no_run
372/// use syncable_cli::analyzer::analyze_project;
373/// use std::path::Path;
374/// 
375/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
376/// let analysis = analyze_project(Path::new("./my-project"))?;
377/// println!("Languages: {:?}", analysis.languages);
378/// # Ok(())
379/// # }
380/// ```
381pub fn analyze_project(path: &Path) -> Result<ProjectAnalysis> {
382    analyze_project_with_config(path, &AnalysisConfig::default())
383}
384
385/// Analyzes a project with custom configuration
386pub fn analyze_project_with_config(path: &Path, config: &AnalysisConfig) -> Result<ProjectAnalysis> {
387    let start_time = std::time::Instant::now();
388    
389    // Validate project path
390    let project_root = crate::common::file_utils::validate_project_path(path)?;
391    
392    log::info!("Starting analysis of project: {}", project_root.display());
393    
394    // Collect project files
395    let files = crate::common::file_utils::collect_project_files(&project_root, config)?;
396    log::debug!("Found {} files to analyze", files.len());
397    
398    // Perform parallel analysis
399    let languages = language_detector::detect_languages(&files, config)?;
400    let frameworks = framework_detector::detect_frameworks(&project_root, &languages, config)?;
401    let dependencies = dependency_parser::parse_dependencies(&project_root, &languages, config)?;
402    let context = context::analyze_context(&project_root, &languages, &frameworks, config)?;
403    
404    // Analyze Docker infrastructure
405    let docker_analysis = analyze_docker_infrastructure(&project_root).ok();
406    
407    let duration = start_time.elapsed();
408    let confidence = calculate_confidence_score(&languages, &frameworks);
409    
410    #[allow(deprecated)]
411    let analysis = ProjectAnalysis {
412        project_root,
413        languages,
414        technologies: frameworks.clone(), // New field with proper technology classification
415        frameworks, // Backward compatibility
416        dependencies,
417        entry_points: context.entry_points,
418        ports: context.ports,
419        environment_variables: context.environment_variables,
420        project_type: context.project_type,
421        build_scripts: context.build_scripts,
422        services: vec![], // TODO: Implement microservice detection
423        architecture_type: ArchitectureType::Monolithic, // TODO: Detect architecture type
424        docker_analysis,
425        analysis_metadata: AnalysisMetadata {
426            timestamp: Utc::now().to_rfc3339(),
427            analyzer_version: env!("CARGO_PKG_VERSION").to_string(),
428            analysis_duration_ms: duration.as_millis() as u64,
429            files_analyzed: files.len(),
430            confidence_score: confidence,
431        },
432    };
433    
434    log::info!("Analysis completed in {}ms", duration.as_millis());
435    Ok(analysis)
436}
437
438/// Calculate overall confidence score based on detection results
439fn calculate_confidence_score(
440    languages: &[DetectedLanguage],
441    frameworks: &[DetectedFramework],
442) -> f32 {
443    if languages.is_empty() {
444        return 0.0;
445    }
446    
447    let lang_confidence: f32 = languages.iter().map(|l| l.confidence).sum::<f32>() / languages.len() as f32;
448    let framework_confidence: f32 = if frameworks.is_empty() {
449        0.5 // Neutral score if no frameworks detected
450    } else {
451        frameworks.iter().map(|f| f.confidence).sum::<f32>() / frameworks.len() as f32
452    };
453    
454    (lang_confidence * 0.7 + framework_confidence * 0.3).min(1.0)
455}
456
457#[cfg(test)]
458mod tests {
459    use super::*;
460    
461    #[test]
462    fn test_confidence_calculation() {
463        let languages = vec![
464            DetectedLanguage {
465                name: "Rust".to_string(),
466                version: Some("1.70.0".to_string()),
467                confidence: 0.9,
468                files: vec![],
469                main_dependencies: vec!["serde".to_string(), "tokio".to_string()],
470                dev_dependencies: vec!["assert_cmd".to_string()],
471                package_manager: Some("cargo".to_string()),
472            }
473        ];
474        
475        let technologies = vec![
476            DetectedTechnology {
477                name: "Actix Web".to_string(),
478                version: Some("4.0".to_string()),
479                category: TechnologyCategory::BackendFramework,
480                confidence: 0.8,
481                requires: vec!["serde".to_string(), "tokio".to_string()],
482                conflicts_with: vec![],
483                is_primary: true,
484            }
485        ];
486        
487        let frameworks = technologies.clone(); // For backward compatibility
488        
489        let score = calculate_confidence_score(&languages, &frameworks);
490        assert!(score > 0.8);
491        assert!(score <= 1.0);
492    }
493    
494    #[test]
495    fn test_empty_analysis() {
496        let languages = vec![];
497        let frameworks = vec![];
498        let score = calculate_confidence_score(&languages, &frameworks);
499        assert_eq!(score, 0.0);
500    }
501}