fob_graph/memory/
queries.rs

1//! Query methods for ModuleGraph.
2
3use std::path::Path;
4
5use super::super::external_dep::ExternalDependency;
6use super::super::{Import, Module, ModuleId};
7use super::graph::ModuleGraph;
8use crate::Result;
9
10impl ModuleGraph {
11    /// Retrieve a module by ID.
12    ///
13    /// Returns an owned `Module`. Internally uses Arc for efficient storage,
14    /// so this clone is inexpensive (only shared data like Vec<Import> are reference-counted).
15    ///
16    /// # Example
17    /// ```
18    /// # use fob_graph::ModuleGraph;
19    /// # fn example(graph: &ModuleGraph, module_id: &fob_graph::ModuleId) -> crate::Result<()> {
20    /// let module = graph.module(module_id)?.unwrap();
21    /// println!("Path: {}", module.path.display());
22    /// # Ok(())
23    /// # }
24    /// ```
25    pub fn module(&self, id: &ModuleId) -> Result<Option<Module>> {
26        let inner = self.inner.read();
27        Ok(inner.modules.get(id).map(|arc| (**arc).clone()))
28    }
29
30    /// Get module by filesystem path.
31    ///
32    /// Returns an owned `Module`. Internally uses Arc for efficient storage,
33    /// so this clone is inexpensive.
34    pub fn module_by_path(&self, path: &Path) -> Result<Option<Module>> {
35        let inner = self.inner.read();
36        Ok(inner
37            .modules
38            .values()
39            .find(|module| module.path == path)
40            .map(|arc| (**arc).clone()))
41    }
42
43    /// Get all modules.
44    ///
45    /// Returns owned `Module` instances. Internally uses Arc for efficient storage,
46    /// so these clones are inexpensive.
47    pub fn modules(&self) -> Result<Vec<Module>> {
48        let inner = self.inner.read();
49        Ok(inner.modules.values().map(|arc| (**arc).clone()).collect())
50    }
51
52    /// Dependencies of a module (forward edges).
53    pub fn dependencies(&self, id: &ModuleId) -> Result<Vec<ModuleId>> {
54        let inner = self.inner.read();
55        Ok(inner
56            .dependencies
57            .get(id)
58            .map(|set| set.iter().cloned().collect())
59            .unwrap_or_default())
60    }
61
62    /// Dependents of a module (reverse edges).
63    pub fn dependents(&self, id: &ModuleId) -> Result<Vec<ModuleId>> {
64        let inner = self.inner.read();
65        Ok(inner
66            .dependents
67            .get(id)
68            .map(|set| set.iter().cloned().collect())
69            .unwrap_or_default())
70    }
71
72    /// Whether a module is present.
73    pub fn contains(&self, id: &ModuleId) -> Result<bool> {
74        let inner = self.inner.read();
75        Ok(inner.modules.contains_key(id))
76    }
77
78    /// Entry points set.
79    pub fn entry_points(&self) -> Result<Vec<ModuleId>> {
80        let inner = self.inner.read();
81        Ok(inner.entry_points.iter().cloned().collect())
82    }
83
84    /// Return total module count.
85    pub fn len(&self) -> Result<usize> {
86        let inner = self.inner.read();
87        Ok(inner.modules.len())
88    }
89
90    /// Check whether graph is empty.
91    pub fn is_empty(&self) -> Result<bool> {
92        let inner = self.inner.read();
93        Ok(inner.modules.is_empty())
94    }
95
96    /// Get imports for a module.
97    pub fn imports_for_module(&self, id: &ModuleId) -> Result<Option<Vec<Import>>> {
98        let inner = self.inner.read();
99        Ok(inner.modules.get(id).map(|m| (*m.imports).clone()))
100    }
101
102    /// Aggregate external dependencies based on import data.
103    pub fn external_dependencies(&self) -> Result<Vec<ExternalDependency>> {
104        let inner = self.inner.read();
105        Ok(inner.external_deps.values().cloned().collect())
106    }
107
108    /// Compute modules with no dependents and no side effects.
109    ///
110    /// Returns owned `Module` instances. Internally uses Arc for efficient storage,
111    /// so these clones are inexpensive.
112    pub fn unreachable_modules(&self) -> Result<Vec<Module>> {
113        let inner = self.inner.read();
114        let mut unreachable = Vec::new();
115
116        for module in inner.modules.values() {
117            if module.is_entry || module.has_side_effects {
118                continue;
119            }
120
121            let has_dependents = inner
122                .dependents
123                .get(&module.id)
124                .map(|deps| !deps.is_empty())
125                .unwrap_or(false);
126
127            if !has_dependents {
128                unreachable.push((**module).clone());
129            }
130        }
131
132        Ok(unreachable)
133    }
134}