arbor_graph/
symbol_table.rs

1use crate::graph::NodeId;
2use std::collections::HashMap;
3use std::path::PathBuf;
4
5/// A global symbol table for resolving cross-file references.
6///
7/// Maps Fully Qualified Names (FQNs) to Node IDs.
8/// Example FQN: "arbor::graph::SymbolTable" -> NodeId(42)
9#[derive(Debug, Default, Clone)]
10pub struct SymbolTable {
11    /// Map of FQN to NodeId
12    by_fqn: HashMap<String, NodeId>,
13
14    /// Map of File Path to list of exported symbols (FQNs)
15    /// Used to resolve wildcard imports or find all symbols in a file.
16    exports_by_file: HashMap<PathBuf, Vec<String>>,
17}
18
19impl SymbolTable {
20    /// Creates a new empty symbol table.
21    pub fn new() -> Self {
22        Self::default()
23    }
24
25    /// Registers a symbol in the table.
26    ///
27    /// * `fqn` - Fully Qualified Name (e.g., "pkg.module.function")
28    /// * `id` - The Node ID in the graph
29    /// * `file` - The file path defining this symbol
30    pub fn insert(&mut self, fqn: String, id: NodeId, file: PathBuf) {
31        self.by_fqn.insert(fqn.clone(), id);
32        self.exports_by_file.entry(file).or_default().push(fqn);
33    }
34
35    /// Resolves a Fully Qualified Name to a Node ID.
36    pub fn resolve(&self, fqn: &str) -> Option<NodeId> {
37        self.by_fqn.get(fqn).copied()
38    }
39
40    /// Returns all symbols exported by a file.
41    pub fn get_file_exports(&self, file: &PathBuf) -> Option<&Vec<String>> {
42        self.exports_by_file.get(file)
43    }
44
45    /// Clears the symbol table.
46    pub fn clear(&mut self) {
47        self.by_fqn.clear();
48        self.exports_by_file.clear();
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    #[test]
57    fn test_insert_resolve() {
58        let mut table = SymbolTable::new();
59        let path = PathBuf::from("main.rs");
60        let id = NodeId::new(1);
61
62        table.insert("main::foo".to_string(), id, path.clone());
63
64        assert_eq!(table.resolve("main::foo"), Some(id));
65        assert_eq!(table.resolve("main::bar"), None);
66
67        let exports = table.get_file_exports(&path).unwrap();
68        assert_eq!(exports.len(), 1);
69        assert_eq!(exports[0], "main::foo");
70    }
71}