debtmap/lib.rs
1//! # Debtmap
2//!
3//! A code complexity and technical debt analyzer that identifies which code to refactor
4//! for maximum cognitive debt reduction and which code to test for maximum risk reduction.
5//!
6//! ## Why Debtmap?
7//!
8//! Unlike traditional static analysis tools that simply flag complex code, debtmap answers two critical questions:
9//!
10//! 1. **"What should I refactor to reduce cognitive burden?"** - Identifies overly complex code that slows down development
11//! 2. **"What should I test first to reduce the most risk?"** - Pinpoints untested complex code that threatens stability
12//!
13//! **Unique Capabilities:**
14//!
15//! - **Coverage-Risk Correlation** - Combines complexity metrics with test coverage to identify genuinely risky code (high complexity + low coverage = critical risk)
16//! - **Reduced False Positives** - Uses entropy analysis and pattern detection to distinguish genuinely complex code from repetitive patterns, reducing false positives by up to 70%
17//! - **Actionable Recommendations** - Provides specific guidance with quantified impact metrics instead of generic warnings
18//! - **Multi-Factor Analysis** - Analyzes complexity, coverage, dependencies, and call graphs for comprehensive prioritization
19//! - **Fast & Open Source** - Written in Rust for 10-100x faster analysis, MIT licensed
20//!
21//! ## Quick Start
22//!
23//! ### Basic File Analysis
24//!
25//! ```rust
26//! use debtmap::{analyzers::get_analyzer, Language};
27//!
28//! // Get language-specific analyzer
29//! let analyzer = get_analyzer(Language::Rust);
30//!
31//! // Parse source code
32//! let content = r#"
33//! fn example() {
34//! if true {
35//! println!("hello");
36//! }
37//! }
38//! "#;
39//! let ast = analyzer.parse(&content, "example.rs".into()).unwrap();
40//!
41//! // Analyze complexity metrics
42//! let metrics = analyzer.analyze(&ast);
43//!
44//! println!("Functions analyzed: {}", metrics.complexity.functions.len());
45//! if !metrics.complexity.functions.is_empty() {
46//! let avg = metrics.complexity.functions.iter()
47//! .map(|f| f.cyclomatic as f64).sum::<f64>()
48//! / metrics.complexity.functions.len() as f64;
49//! println!("Average complexity: {:.2}", avg);
50//! }
51//! ```
52//!
53//! ### Code Smell Detection
54//!
55//! ```rust
56//! use debtmap::debt::patterns::find_code_smells;
57//! use std::path::Path;
58//!
59//! let content = r#"
60//! fn example() {
61//! // TODO: Fix this later
62//! let x = 1;
63//! }
64//! "#;
65//!
66//! // Find TODOs, FIXMEs, and other code smells
67//! let smells = find_code_smells(&content, Path::new("example.rs"));
68//! for smell in smells {
69//! println!("{:?} at line {}", smell.debt_type, smell.line);
70//! }
71//! ```
72//!
73//! ### Coverage-Based Risk Analysis
74//!
75//! ```rust,ignore
76//! use debtmap::{
77//! analyzers::get_analyzer,
78//! risk::{lcov::parse_lcov_file, RiskAnalyzer},
79//! Language,
80//! };
81//! use std::path::PathBuf;
82//!
83//! // Parse coverage data (skip if file doesn't exist)
84//! let coverage_path = std::path::Path::new("target/coverage/lcov.info");
85//! if !coverage_path.exists() {
86//! println!("Generate coverage with: cargo llvm-cov --lcov --output-path target/coverage/lcov.info");
87//! return;
88//! }
89//!
90//! let coverage_data = parse_lcov_file(coverage_path).unwrap();
91//!
92//! // Analyze a file
93//! let analyzer = get_analyzer(Language::Rust);
94//! let content = std::fs::read_to_string("src/main.rs").unwrap();
95//! let ast = analyzer.parse(&content, "src/main.rs".into()).unwrap();
96//! let metrics = analyzer.analyze(&ast);
97//!
98//! // Calculate risk scores for each function
99//! let risk_analyzer = RiskAnalyzer::default();
100//! let file_path = std::path::Path::new("src/main.rs");
101//! for func in &metrics.complexity.functions {
102//! let coverage = coverage_data.get_function_coverage(file_path, &func.name);
103//! let risk = risk_analyzer.analyze_function(
104//! PathBuf::from("src/main.rs"),
105//! func.name.clone(),
106//! (func.start_line, func.end_line),
107//! &func.complexity,
108//! coverage,
109//! false,
110//! );
111//! if risk.risk_score > 5.0 {
112//! println!("HIGH RISK: {} (score: {:.1})", risk.function_name, risk.risk_score);
113//! }
114//! }
115//! ```
116//!
117//! ## Features
118//!
119//! ### Multi-Language Support
120//!
121//! Debtmap analyzes code across multiple programming languages:
122//!
123//! - **Rust** - Full support with comprehensive AST analysis using [`syn`](https://docs.rs/syn)
124//! - **Python** - Partial support via [`rustpython-parser`](https://docs.rs/rustpython-parser)
125//!
126//! ### Performance Characteristics
127//!
128//! - **Parallel Processing** - Uses [`rayon`](https://docs.rs/rayon) for CPU-intensive analysis across multiple files
129//! - **Concurrent Data Structures** - Leverages [`dashmap`](https://docs.rs/dashmap) for lock-free concurrent access
130//! - **Immutable Collections** - Uses [`im`](https://docs.rs/im) crate for persistent data structures
131//! - **Performance** - 10-100x faster than Java/Python-based competitors
132//!
133//! ### Coverage Integration
134//!
135//! Debtmap works with any tool generating LCOV format:
136//! - **Rust**: [`cargo-llvm-cov`](https://github.com/taiki-e/cargo-llvm-cov) (recommended), [`cargo-tarpaulin`](https://github.com/xd009642/tarpaulin)
137//! - **Python**: `pytest-cov`, `coverage.py`
138//! - **JavaScript**: `jest --coverage`, `nyc`
139//!
140//! ## Architecture
141//!
142//! Debtmap follows a functional architecture with clear separation of concerns:
143//!
144//! ```text
145//! ┌─────────────────────────────────────────────────────────────┐
146//! │ Input Layer (I/O) │
147//! │ File Discovery → Content Reading → Coverage Parsing │
148//! └────────────────────────┬────────────────────────────────────┘
149//! ↓
150//! ┌─────────────────────────────────────────────────────────────┐
151//! │ Parser Layer (Pure) │
152//! │ Language Detection → AST Generation → Symbol Extraction │
153//! └────────────────────────┬────────────────────────────────────┘
154//! ↓
155//! ┌─────────────────────────────────────────────────────────────┐
156//! │ Analysis Layer (Pure) │
157//! │ Complexity → Debt Detection → Risk Assessment → Dependency │
158//! └────────────────────────┬────────────────────────────────────┘
159//! ↓
160//! ┌─────────────────────────────────────────────────────────────┐
161//! │ Aggregation Layer (Functional) │
162//! │ Combine Results → Priority Scoring → Recommendation Gen │
163//! └────────────────────────┬────────────────────────────────────┘
164//! ↓
165//! ┌─────────────────────────────────────────────────────────────┐
166//! │ Output Layer (I/O) │
167//! │ Format Selection → Report Generation → File Writing │
168//! └─────────────────────────────────────────────────────────────┘
169//! ```
170//!
171//! ### Core Modules
172//!
173//! - **[`analyzers`]** - Language-specific parsers and AST analysis
174//! - [`analyzers::get_analyzer`] - Factory for language-specific analyzers
175//! - [`analyzers::analyze_file`] - High-level file analysis API
176//!
177//! - **[`debt`]** - Technical debt pattern detection
178//! - [`debt::patterns`] - Code smell detection (TODOs, magic numbers, etc.)
179//! - [`debt::smells`] - Function-level smell analysis (long methods, deep nesting)
180//! - [`debt::duplication`] - Duplicate code detection
181//! - [`debt::coupling`] - Module coupling analysis
182//!
183//! - **[`risk`]** - Risk assessment and prioritization
184//! - [`risk::RiskAnalyzer`] - Coverage-based risk scoring
185//! - [`risk::lcov::parse_lcov_file`] - LCOV format parser
186//! - [`risk::insights::generate_risk_insights`] - Actionable recommendation generation
187//!
188//! - **[`complexity`]** - Complexity metric calculations
189//! - Cyclomatic complexity (control flow branching)
190//! - Cognitive complexity (human comprehension difficulty)
191//! - Halstead metrics (vocabulary and volume)
192//!
193//! - **[`io`]** - Input/output formatting and handling
194//! - [`io::output`] - Multiple output formats (JSON, YAML, table)
195//! - [`io::output::OutputWriter`] - Trait for custom output formats
196//!
197//! - **[`analysis`]** - Advanced analysis algorithms
198//! - [`analysis::RustCallGraph`] - Function call graph construction
199//! - [`analysis::DeadCodeAnalysis`] - Unused code detection
200//! - [`analysis::FrameworkPatternDetector`] - Framework-specific patterns
201//!
202//! - **[`testing`]** - Test quality analysis
203//! - Test coverage correlation
204//! - Test effectiveness scoring
205//!
206//! ### Data Flow Principles
207//!
208//! Debtmap is built on functional programming principles:
209//!
210//! 1. **Pure Core** - All analysis logic is pure functions with no side effects
211//! 2. **I/O at Boundaries** - File operations and network calls isolated to edges
212//! 3. **Immutable Data** - Uses persistent data structures for safe concurrent access
213//! 4. **Function Composition** - Complex behavior built from simple, testable units
214//! 5. **Parallel Processing** - Embarrassingly parallel analysis across files
215//!
216//! ## CLI Usage
217//!
218//! For command-line usage, see the [CLI Reference](https://iepathos.github.io/debtmap/cli-reference.html).
219//!
220//! ```bash
221//! # Basic analysis
222//! debtmap analyze .
223//!
224//! # With coverage integration
225//! cargo llvm-cov --lcov --output-path target/coverage/lcov.info
226//! debtmap analyze . --lcov target/coverage/lcov.info
227//!
228//! # Generate JSON report
229//! debtmap analyze . --format json --output report.json
230//! ```
231//!
232//! ## Examples
233//!
234//! ### Custom Complexity Thresholds
235//!
236//! ```rust,no_run
237//! use debtmap::{analyzers::get_analyzer, Language};
238//!
239//! let analyzer = get_analyzer(Language::Rust);
240//! let content = std::fs::read_to_string("src/main.rs").unwrap();
241//! let ast = analyzer.parse(&content, "src/main.rs".into()).unwrap();
242//! let metrics = analyzer.analyze(&ast);
243//!
244//! // Filter functions by custom complexity threshold
245//! let high_complexity_threshold = 10;
246//! let complex_functions: Vec<_> = metrics.complexity.functions.iter()
247//! .filter(|f| f.cyclomatic > high_complexity_threshold)
248//! .collect();
249//!
250//! println!("Found {} highly complex functions", complex_functions.len());
251//! for func in complex_functions {
252//! println!(" {} (complexity: {})", func.name, func.cyclomatic);
253//! }
254//! ```
255//!
256//! ### Detecting Circular Dependencies
257//!
258//! ```rust,no_run
259//! use debtmap::debt::circular::analyze_module_dependencies;
260//! use debtmap::core::Dependency;
261//! use std::path::PathBuf;
262//!
263//! // Example: Analyze module dependencies from parsed files
264//! // In practice, you would gather dependencies during file parsing
265//! let files: Vec<(PathBuf, Vec<Dependency>)> = vec![
266//! (PathBuf::from("src/main.rs"), vec![]),
267//! (PathBuf::from("src/lib.rs"), vec![]),
268//! ];
269//!
270//! let _dependency_graph = analyze_module_dependencies(&files);
271//! // The dependency graph can be used to detect circular dependencies
272//! // and analyze module coupling
273//! ```
274//!
275//! ### Generating Risk Insights
276//!
277//! ```rust,ignore
278//! use debtmap::{
279//! analyzers::get_analyzer,
280//! risk::{lcov::parse_lcov_file, RiskAnalyzer, insights::generate_risk_insights},
281//! Language,
282//! };
283//! use std::path::PathBuf;
284//! use im::Vector;
285//!
286//! // Parse coverage and analyze file
287//! let coverage = parse_lcov_file(std::path::Path::new("target/coverage/lcov.info")).unwrap();
288//! let analyzer = get_analyzer(Language::Rust);
289//! let content = std::fs::read_to_string("src/main.rs").unwrap();
290//! let ast = analyzer.parse(&content, "src/main.rs".into()).unwrap();
291//! let metrics = analyzer.analyze(&ast);
292//!
293//! // Calculate risks for all functions
294//! let risk_analyzer = RiskAnalyzer::default();
295//! let mut risks = Vector::new();
296//! let file_path = std::path::Path::new("src/main.rs");
297//! for func in &metrics.complexity.functions {
298//! let coverage_pct = coverage.get_function_coverage(file_path, &func.name);
299//! let risk = risk_analyzer.analyze_function(
300//! PathBuf::from("src/main.rs"),
301//! func.name.clone(),
302//! (func.start_line, func.end_line),
303//! &func.complexity,
304//! coverage_pct,
305//! false,
306//! );
307//! risks.push_back(risk);
308//! }
309//!
310//! // Generate actionable insights
311//! let insights = generate_risk_insights(risks, &risk_analyzer);
312//!
313//! // Display top recommendations
314//! for rec in insights.risk_reduction_opportunities.iter().take(5) {
315//! println!("{}", rec.recommendation);
316//! }
317//! ```
318//!
319//! ## Resources
320//!
321//! - **Documentation**: [iepathos.github.io/debtmap](https://iepathos.github.io/debtmap/)
322//! - **Repository**: [github.com/iepathos/debtmap](https://github.com/iepathos/debtmap)
323//! - **Crate**: [crates.io/crates/debtmap](https://crates.io/crates/debtmap)
324//! - **Issues**: [github.com/iepathos/debtmap/issues](https://github.com/iepathos/debtmap/issues)
325//!
326//! ## License
327//!
328//! Debtmap is licensed under the [MIT License](https://github.com/iepathos/debtmap/blob/master/LICENSE).
329
330// Export modules for library usage
331pub mod analysis;
332pub mod analysis_utils;
333pub mod analyzers;
334pub mod builders;
335pub mod cli;
336pub mod commands;
337pub mod common;
338pub mod comparison;
339pub mod complexity;
340pub mod config;
341pub mod context;
342pub mod core;
343pub mod data_flow;
344pub mod database;
345pub mod debt;
346pub mod debtmap_error;
347pub mod di;
348pub mod effects;
349pub mod env;
350pub mod error;
351pub mod errors;
352pub mod extraction;
353pub mod extraction_patterns;
354pub mod formatting;
355pub mod io;
356pub mod metrics;
357pub mod observability;
358pub mod organization;
359pub mod output;
360pub mod patterns;
361pub mod pipeline;
362pub mod priority;
363pub mod progress;
364pub mod refactoring;
365pub mod resource;
366pub mod resources;
367pub mod risk;
368pub mod testing;
369pub mod testkit;
370pub mod transformers;
371pub mod tui;
372pub mod utils;
373
374// Re-export commonly used types
375pub use crate::core::{
376 AnalysisResults, CircularDependency, ComplexityMetrics, ComplexityReport, ComplexitySummary,
377 DebtItem, DebtType, Dependency, DependencyKind, DependencyReport, DuplicationBlock,
378 DuplicationLocation, FileMetrics, FunctionMetrics, Language, ModuleDependency, Priority,
379 TechnicalDebtReport,
380};
381
382pub use crate::debt::{
383 circular::{analyze_module_dependencies, DependencyGraph},
384 coupling::{calculate_coupling_metrics, identify_coupling_issues, CouplingMetrics},
385 duplication::detect_duplication,
386 patterns::{
387 detect_duplicate_strings, find_code_smells, find_code_smells_with_suppression,
388 find_todos_and_fixmes, find_todos_and_fixmes_with_suppression,
389 },
390 smells::{
391 analyze_function_smells, analyze_module_smells, detect_deep_nesting, detect_long_method,
392 detect_long_parameter_list, CodeSmell, SmellType,
393 },
394 suppression::{parse_suppression_comments, SuppressionContext, SuppressionStats},
395};
396
397pub use crate::core::metrics::{
398 calculate_average_complexity, count_high_complexity, find_max_complexity,
399};
400
401pub use crate::io::output::{create_writer, OutputFormat, OutputWriter};
402
403pub use crate::analyzers::{analyze_file, get_analyzer, Analyzer};
404
405pub use crate::risk::{
406 insights::generate_risk_insights, lcov::parse_lcov_file, FunctionRisk, RiskAnalyzer,
407 RiskCategory, RiskInsight, TestingRecommendation,
408};
409
410pub use crate::analysis::{
411 AnalysisConfig, CrossModuleTracker, DeadCodeAnalysis, FrameworkPatternDetector,
412 FunctionPointerTracker, RustCallGraph, RustCallGraphBuilder, TraitRegistry,
413};
414
415// Stillwater integration: Effect system and environment (spec 195)
416pub use crate::effects::{
417 combine_validations, effect_fail, effect_from_fn, effect_pure, run_effect, run_effect_async,
418 run_validation, validation_failure, validation_failures, validation_map, validation_success,
419 AnalysisEffect, AnalysisErrors, AnalysisValidation,
420};
421pub use crate::env::{AnalysisEnv, RealEnv};
422pub use crate::errors::{errors_to_anyhow, format_error_list, AnalysisError};
423
424// Reader pattern helpers (spec 199) - zero-cost config access without parameter threading
425pub use crate::effects::{
426 ask_env, asks_config, asks_entropy, asks_scoring, asks_thresholds, local_with_config,
427};
428
429// Progress effects (spec 262) - composable progress reporting
430pub use crate::effects::progress::{
431 par_traverse_with_progress, report_progress, traverse_with_progress, warn_progress, with_stage,
432};
433
434// Progress traits and implementations (spec 262)
435pub use crate::progress::traits::{HasProgress, ProgressSink};
436pub use crate::progress::{
437 CliProgressSink, ProgressEvent, RecordingProgressSink, SilentProgressSink,
438};
439
440// Bracket pattern resource management (spec 206)
441pub use crate::resources::{
442 bracket_io, with_file_read, with_lock_file, with_progress, with_spinner, with_temp_dir,
443 FileHandle, LockFile, ProgressHandle, TempDir,
444};
445
446// Testing infrastructure (spec 200) - MockEnv and test helpers
447// Note: Assertion macros are exported via #[macro_export] in testkit::assertions
448pub use crate::testkit::{ConfigBuilder, DebtmapTestEnv};
449
450// Observability infrastructure (spec 207) - panic hook and context tracking
451pub use crate::observability::{
452 get_current_context, get_progress, increment_processed, install_panic_hook, set_current_file,
453 set_phase, set_phase_persistent, set_progress, AnalysisContext, AnalysisPhase,
454};
455
456// Unified error types (spec 005) - consolidated error handling with context chaining
457pub use crate::debtmap_error::{DebtmapError, ErrorCode};