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