Skip to main content

factorio_ir/prune/
module_graph.rs

1use std::collections::HashMap;
2
3use crate::module::Module;
4
5/// Identifies a single transpiled item within a module for reachability tracking.
6#[derive(Debug, Clone, PartialEq, Eq, Hash)]
7pub enum ItemKey {
8    /// A top-level or nested function by name.
9    Function(String),
10    /// A struct table by name.
11    Struct(String),
12    /// An instance method on a struct: `(struct_name, method_name)`.
13    StructMethod(String, String),
14    /// An associated constant on a struct: `(struct_name, constant_name)`.
15    StructConstant(String, String),
16}
17
18/// Cross-module index built from lowered modules before pruning.
19pub struct ModuleGraph<'a> {
20    pub modules: &'a [Module],
21    by_name: HashMap<&'a str, &'a Module>,
22    children: HashMap<&'a str, Vec<&'a str>>,
23}
24
25impl<'a> ModuleGraph<'a> {
26    /// Build a lookup graph from all modules in a build.
27    #[must_use]
28    pub fn new(modules: &'a [Module]) -> Self {
29        let by_name = modules
30            .iter()
31            .map(|module| (module.name.as_str(), module))
32            .collect::<HashMap<_, _>>();
33        let mut children = HashMap::<&str, Vec<&str>>::new();
34        for module in modules {
35            for child in &module.submodules {
36                if let Some(child_module) = by_name.get(child.as_str()) {
37                    children
38                        .entry(module.name.as_str())
39                        .or_default()
40                        .push(child_module.name.as_str());
41                }
42            }
43        }
44        Self {
45            modules,
46            by_name,
47            children,
48        }
49    }
50
51    /// Look up a module by its dotted name (`control`, `shared.player`, etc.).
52    #[must_use]
53    pub fn get(&self, name: &str) -> Option<&'a Module> {
54        self.by_name.get(name).copied()
55    }
56
57    /// Return the direct submodule names declared by `name`.
58    pub fn child_modules(&self, name: &str) -> &[&'a str] {
59        self.children.get(name).map_or(&[], Vec::as_slice)
60    }
61}