fob_graph/analysis/walker/
mod.rs

1//! Graph walker for dependency traversal.
2//!
3//! Performs BFS traversal of the import graph, parsing modules and building
4//! a CollectionState that can be converted to a ModuleGraph.
5
6mod parser;
7mod traversal;
8mod validation;
9
10pub use validation::{PathTraversalError, normalize_and_validate_path, validate_path_within_cwd};
11
12use std::sync::Arc;
13
14use crate::collection::CollectionState;
15use crate::runtime::Runtime;
16
17use crate::analysis::config::AnalyzerConfig;
18use crate::analysis::resolver::ModuleResolver;
19
20/// Error that can occur during graph walking.
21#[derive(Debug, thiserror::Error)]
22pub enum WalkerError {
23    #[error("Failed to read file '{path}': {source}")]
24    ReadFile {
25        path: std::path::PathBuf,
26        #[source]
27        source: crate::runtime::RuntimeError,
28    },
29
30    #[error("Maximum depth exceeded: {depth}")]
31    MaxDepthExceeded { depth: usize },
32
33    #[error("Circular dependency detected: {path}")]
34    CircularDependency { path: std::path::PathBuf },
35
36    #[error("Failed to resolve module '{specifier}' from '{from}': {reason}")]
37    ResolutionFailed {
38        specifier: String,
39        from: std::path::PathBuf,
40        reason: String,
41    },
42
43    #[error("Failed to extract scripts from '{path}': {source}")]
44    ExtractionFailed {
45        path: std::path::PathBuf,
46        #[source]
47        source: crate::analysis::extractors::ExtractorError,
48    },
49
50    #[error("Path traversal detected: path '{path}' escapes from cwd '{cwd}'")]
51    PathTraversal {
52        path: std::path::PathBuf,
53        cwd: std::path::PathBuf,
54    },
55
56    #[error("Too many modules processed: {count} modules (max: {max} allowed)")]
57    TooManyModules { count: usize, max: usize },
58
59    #[error("File too large: {path} is {size} bytes (max: {max} bytes)")]
60    FileTooLarge {
61        path: std::path::PathBuf,
62        size: usize,
63        max: usize,
64    },
65}
66
67/// Graph walker that traverses the dependency graph.
68pub struct GraphWalker {
69    resolver: ModuleResolver,
70    config: AnalyzerConfig,
71}
72
73impl GraphWalker {
74    /// Create a new graph walker with the given configuration.
75    pub fn new(config: AnalyzerConfig) -> Self {
76        let resolver = ModuleResolver::new(config.clone());
77        Self { resolver, config }
78    }
79
80    /// Walk the dependency graph starting from entry points.
81    ///
82    /// Returns a CollectionState that can be converted to a ModuleGraph.
83    pub async fn walk(&self, runtime: Arc<dyn Runtime>) -> Result<CollectionState, WalkerError> {
84        let traversal = traversal::Traversal::new(&self.resolver, &self.config);
85        traversal.traverse(runtime).await
86    }
87}