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