pyo3_stub_gen/docgen/
export.rs

1//! Export resolution for determining which items are publicly accessible
2
3use crate::docgen::util::is_hidden_module;
4use crate::generate::Module;
5use std::collections::{BTreeMap, BTreeSet};
6
7/// Resolver for determining which items are exported from modules
8pub struct ExportResolver<'a> {
9    modules: &'a BTreeMap<String, Module>,
10}
11
12impl<'a> ExportResolver<'a> {
13    pub fn new(modules: &'a BTreeMap<String, Module>) -> Self {
14        Self { modules }
15    }
16
17    /// Resolve which items are exported from a module
18    /// Rules:
19    /// 1. If __all__ exists (re-exports or verbatim): use it
20    /// 2. Otherwise: all non-underscore items
21    /// 3. Add re-exported items
22    /// 4. Add verbatim entries
23    /// 5. Remove excluded items
24    pub fn resolve_exports(&self, module: &Module) -> BTreeSet<String> {
25        let mut exports = BTreeSet::new();
26
27        // Add directly-defined public items
28        for name in module.function.keys() {
29            if !name.starts_with('_') {
30                exports.insert((*name).to_string());
31            }
32        }
33
34        for class in module.class.values() {
35            if !class.name.starts_with('_') {
36                exports.insert(class.name.to_string());
37            }
38        }
39
40        for enum_def in module.enum_.values() {
41            if !enum_def.name.starts_with('_') {
42                exports.insert(enum_def.name.to_string());
43            }
44        }
45
46        for type_alias in module.type_aliases.keys() {
47            if !type_alias.starts_with('_') {
48                exports.insert((*type_alias).to_string());
49            }
50        }
51
52        for var in module.variables.keys() {
53            if !var.starts_with('_') {
54                exports.insert((*var).to_string());
55            }
56        }
57
58        for submod in &module.submodules {
59            if !submod.starts_with('_') {
60                exports.insert(submod.to_string());
61            }
62        }
63
64        // Add items from module re-exports (from reexport_module_members!)
65        for re_export in &module.module_re_exports {
66            exports.extend(re_export.items.iter().cloned());
67        }
68
69        // Add verbatim entries (allows explicitly exporting underscore items)
70        exports.extend(module.verbatim_all_entries.iter().cloned());
71
72        // Remove excluded items
73        for excluded in &module.excluded_all_entries {
74            exports.remove(excluded);
75        }
76
77        exports
78    }
79
80    /// Build global map: item_fqn → module_where_exported
81    /// For re-exports: map to re-exporting module
82    pub fn build_export_map(&self) -> BTreeMap<String, String> {
83        let mut export_map = BTreeMap::new();
84
85        for (module_name, module) in self.modules {
86            // Skip hidden modules (any component starts with '_')
87            if is_hidden_module(module_name) {
88                continue;
89            }
90
91            let exports = self.resolve_exports(module);
92
93            for item_name in exports {
94                let fqn = format!("{}.{}", module_name, item_name);
95                export_map.insert(fqn, module_name.clone());
96            }
97        }
98
99        export_map
100    }
101}