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