fob_graph/analysis/
result.rs

1use std::fmt;
2
3use crate::{
4    ExternalDependency, GraphStatistics, ModuleGraph, ModuleId, UnusedExport,
5    dependency_chain::DependencyChain, symbol::SymbolStatistics,
6};
7
8#[derive(Debug)]
9pub struct AnalysisResult {
10    pub graph: ModuleGraph,
11    pub entry_points: Vec<ModuleId>,
12    pub warnings: Vec<String>,
13    pub errors: Vec<String>,
14    pub stats: GraphStatistics,
15    /// Symbol-level statistics from intra-file dead code analysis
16    pub symbol_stats: SymbolStatistics,
17}
18
19impl AnalysisResult {
20    /// Get all unused exports in the module graph.
21    pub fn unused_exports(&self) -> crate::Result<Vec<UnusedExport>> {
22        self.graph.unused_exports()
23    }
24
25    /// Get all external dependencies.
26    pub fn external_dependencies(&self) -> crate::Result<Vec<ExternalDependency>> {
27        self.graph.external_dependencies()
28    }
29
30    /// Check if the analysis completed without errors.
31    pub fn is_ok(&self) -> bool {
32        self.errors.is_empty()
33    }
34
35    /// Check if there are any warnings.
36    pub fn has_warnings(&self) -> bool {
37        !self.warnings.is_empty()
38    }
39
40    /// Get all dependency chains to a target module.
41    ///
42    /// Useful for understanding why a module is included or finding circular dependencies.
43    pub fn dependency_chains_to(&self, target: &ModuleId) -> crate::Result<Vec<DependencyChain>> {
44        self.graph.dependency_chains_to(target)
45    }
46
47    /// Find circular dependencies in the module graph.
48    ///
49    /// Returns chains that contain cycles (same module appears multiple times).
50    pub fn find_circular_dependencies(&self) -> crate::Result<Vec<DependencyChain>> {
51        let modules = self.graph.modules()?;
52        let mut circular = Vec::new();
53
54        for module in modules {
55            let chains = self.graph.dependency_chains_to(&module.id)?;
56            for chain in chains {
57                if chain.has_cycle() {
58                    circular.push(chain);
59                }
60            }
61        }
62
63        Ok(circular)
64    }
65}
66
67impl fmt::Display for AnalysisResult {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        writeln!(f, "Analysis Result")?;
70        writeln!(f, "================")?;
71        writeln!(f, "Entry points: {}", self.entry_points.len())?;
72
73        if let Ok(modules) = self.graph.modules() {
74            writeln!(f, "Total modules: {}", modules.len())?;
75        }
76
77        if let Ok(unused) = self.unused_exports() {
78            writeln!(f, "Unused exports: {}", unused.len())?;
79        }
80
81        if let Ok(external) = self.external_dependencies() {
82            writeln!(f, "External dependencies: {}", external.len())?;
83        }
84
85        writeln!(f, "Warnings: {}", self.warnings.len())?;
86        writeln!(f, "Errors: {}", self.errors.len())?;
87
88        if !self.warnings.is_empty() {
89            writeln!(f, "\nWarnings:")?;
90            for warning in &self.warnings {
91                writeln!(f, "  - {}", warning)?;
92            }
93        }
94
95        if !self.errors.is_empty() {
96            writeln!(f, "\nErrors:")?;
97            for error in &self.errors {
98                writeln!(f, "  - {}", error)?;
99            }
100        }
101
102        Ok(())
103    }
104}