1use crate::Decl;
6use std::collections::{HashMap, HashSet};
7
8#[derive(Debug, Clone)]
10pub struct Module {
11 pub name: String,
13 pub path: Vec<String>,
15 pub decls: Vec<Decl>,
17 pub imports: Vec<String>,
19 pub exports: Vec<String>,
21 pub import_specs: Vec<ImportSpec>,
23 pub export_spec: Option<ExportSpec>,
25 pub namespaces: Vec<NamespaceScope>,
27 pub opens: Vec<OpenDirective>,
29 pub visibility_map: HashMap<String, Visibility>,
31 pub config: ModuleConfig,
33}
34impl Module {
35 pub fn new(name: String) -> Self {
37 Self {
38 name,
39 path: Vec::new(),
40 decls: Vec::new(),
41 imports: Vec::new(),
42 exports: Vec::new(),
43 import_specs: Vec::new(),
44 export_spec: None,
45 namespaces: Vec::new(),
46 opens: Vec::new(),
47 visibility_map: HashMap::new(),
48 config: ModuleConfig {
49 allow_circular: false,
50 private_by_default: false,
51 allow_shadowing: false,
52 },
53 }
54 }
55 pub fn with_config(name: String, config: ModuleConfig) -> Self {
57 let mut module = Self::new(name);
58 module.config = config;
59 module
60 }
61 pub fn add_decl(&mut self, decl: Decl) {
63 self.decls.push(decl);
64 }
65 pub fn add_import(&mut self, module: String) {
67 self.imports.push(module);
68 }
69 pub fn add_export(&mut self, name: String) {
71 self.exports.push(name);
72 }
73 pub fn full_path(&self) -> String {
75 if self.path.is_empty() {
76 self.name.clone()
77 } else {
78 format!("{}.{}", self.path.join("."), self.name)
79 }
80 }
81 #[allow(dead_code)]
86 pub fn resolve_name(&self, name: &str) -> ResolvedName {
87 let local_names = self.declared_names();
88 if local_names.contains(&name.to_string()) {
89 return ResolvedName::Local(name.to_string());
90 }
91 for ns in &self.namespaces {
92 if let Some(full) = ns.aliases.get(name) {
93 return ResolvedName::Local(full.clone());
94 }
95 }
96 let mut found_sources: Vec<String> = Vec::new();
97 for spec in &self.import_specs {
98 match spec {
99 ImportSpec::All(module_name) => {
100 found_sources.push(module_name.clone());
101 }
102 ImportSpec::Selective(module_name, selected) => {
103 if selected.contains(&name.to_string()) {
104 found_sources.push(module_name.clone());
105 }
106 }
107 ImportSpec::Hiding(module_name, hidden) => {
108 if !hidden.contains(&name.to_string()) {
109 found_sources.push(module_name.clone());
110 }
111 }
112 ImportSpec::Renaming(module_name, renamings) => {
113 for (from, to) in renamings {
114 if to == name {
115 return ResolvedName::Imported {
116 module: module_name.clone(),
117 name: from.clone(),
118 };
119 }
120 }
121 }
122 }
123 }
124 if !found_sources.is_empty() {
125 if found_sources.len() == 1 {
126 return ResolvedName::Imported {
127 module: found_sources[0].clone(),
128 name: name.to_string(),
129 };
130 }
131 return ResolvedName::Ambiguous(found_sources);
132 }
133 if !self.imports.is_empty() {
134 let mut import_sources: Vec<String> = Vec::new();
135 for imp in &self.imports {
136 import_sources.push(imp.clone());
137 }
138 if import_sources.len() == 1 {
139 return ResolvedName::Imported {
140 module: import_sources[0].clone(),
141 name: name.to_string(),
142 };
143 }
144 if import_sources.len() > 1 {
145 return ResolvedName::Ambiguous(import_sources);
146 }
147 }
148 ResolvedName::NotFound
149 }
150 fn declared_names(&self) -> Vec<String> {
152 let mut names = Vec::new();
153 for decl in &self.decls {
154 if let Some(name) = Self::decl_name(decl) {
155 names.push(name);
156 }
157 }
158 names
159 }
160 fn decl_name(decl: &Decl) -> Option<String> {
162 match decl {
163 Decl::Axiom { name, .. } => Some(name.clone()),
164 Decl::Definition { name, .. } => Some(name.clone()),
165 Decl::Theorem { name, .. } => Some(name.clone()),
166 Decl::Inductive { name, .. } => Some(name.clone()),
167 Decl::Namespace { name, .. } => Some(name.clone()),
168 Decl::Structure { name, .. } => Some(name.clone()),
169 Decl::ClassDecl { name, .. } => Some(name.clone()),
170 Decl::InstanceDecl {
171 name, class_name, ..
172 } => name.clone().or_else(|| Some(class_name.clone())),
173 Decl::SectionDecl { name, .. } => Some(name.clone()),
174 Decl::Mutual { .. } => None,
175 Decl::Derive { type_name, .. } => Some(type_name.clone()),
176 Decl::NotationDecl { name, .. } => Some(name.clone()),
177 Decl::Universe { .. } => None,
178 Decl::Import { .. }
179 | Decl::Variable { .. }
180 | Decl::Open { .. }
181 | Decl::Attribute { .. }
182 | Decl::HashCmd { .. } => None,
183 }
184 }
185 #[allow(dead_code)]
187 pub fn visible_names(&self) -> Vec<String> {
188 let mut names = self.declared_names();
189 for ns in &self.namespaces {
190 for alias in ns.aliases.keys() {
191 if !names.contains(alias) {
192 names.push(alias.clone());
193 }
194 }
195 }
196 names
197 }
198 #[allow(dead_code)]
200 pub fn exported_names(&self) -> Vec<String> {
201 match &self.export_spec {
202 Some(ExportSpec::All) => self.declared_names(),
203 Some(ExportSpec::Selective(selected)) => {
204 let declared = self.declared_names();
205 selected
206 .iter()
207 .filter(|n| declared.contains(n))
208 .cloned()
209 .collect()
210 }
211 None => {
212 if self.exports.is_empty() {
213 self.declared_names()
214 } else {
215 self.exports.clone()
216 }
217 }
218 }
219 }
220 #[allow(dead_code)]
222 pub fn add_namespace(&mut self, ns: NamespaceScope) {
223 self.namespaces.push(ns);
224 }
225 #[allow(dead_code)]
227 pub fn with_import_spec(&mut self, spec: ImportSpec) {
228 let module_name = match &spec {
229 ImportSpec::All(m) => m.clone(),
230 ImportSpec::Selective(m, _) => m.clone(),
231 ImportSpec::Hiding(m, _) => m.clone(),
232 ImportSpec::Renaming(m, _) => m.clone(),
233 };
234 if !self.imports.contains(&module_name) {
235 self.imports.push(module_name);
236 }
237 self.import_specs.push(spec);
238 }
239 #[allow(dead_code)]
241 pub fn set_visibility(&mut self, name: String, visibility: Visibility) {
242 self.visibility_map.insert(name, visibility);
243 }
244 #[allow(dead_code)]
246 pub fn get_visibility(&self, name: &str) -> Visibility {
247 self.visibility_map
248 .get(name)
249 .copied()
250 .unwrap_or(if self.config.private_by_default {
251 Visibility::Private
252 } else {
253 Visibility::Public
254 })
255 }
256 #[allow(dead_code)]
258 pub fn add_open(&mut self, module: String, scoped: bool) {
259 self.opens.push(OpenDirective { module, scoped });
260 }
261 #[allow(dead_code)]
263 pub fn get_opens(&self) -> Vec<String> {
264 self.opens.iter().map(|o| o.module.clone()).collect()
265 }
266 #[allow(dead_code)]
268 pub fn resolve_with_opens(&self, name: &str) -> ResolvedName {
269 if let ResolvedName::Local(n) = self.resolve_name(name) {
270 return ResolvedName::Local(n);
271 }
272 let mut found_sources = Vec::new();
273 for open in &self.opens {
274 found_sources.push(open.module.clone());
275 }
276 if !found_sources.is_empty() {
277 if found_sources.len() == 1 {
278 return ResolvedName::Imported {
279 module: found_sources[0].clone(),
280 name: name.to_string(),
281 };
282 }
283 return ResolvedName::Ambiguous(found_sources);
284 }
285 self.resolve_name(name)
286 }
287 #[allow(dead_code)]
289 pub fn all_visible_names(&self) -> Vec<NameVisibility> {
290 let mut result = Vec::new();
291 let declared = self.declared_names();
292 for name in declared {
293 let visibility = self.get_visibility(&name);
294 result.push(NameVisibility {
295 name,
296 visibility,
297 from_module: None,
298 });
299 }
300 for open in &self.opens {
301 result.push(NameVisibility {
302 name: format!("{}::*", open.module),
303 visibility: Visibility::Public,
304 from_module: Some(open.module.clone()),
305 });
306 }
307 result
308 }
309 #[allow(dead_code)]
311 pub fn is_accessible(&self, name: &str, from_same_module: bool) -> bool {
312 let visibility = self.get_visibility(name);
313 match visibility {
314 Visibility::Public => true,
315 Visibility::Private => from_same_module,
316 Visibility::Protected => true,
317 }
318 }
319 #[allow(dead_code)]
321 pub fn get_dependencies(&self) -> HashSet<String> {
322 let mut deps = HashSet::new();
323 deps.extend(self.imports.clone());
324 for open in &self.opens {
325 deps.insert(open.module.clone());
326 }
327 for spec in &self.import_specs {
328 let mod_name = match spec {
329 ImportSpec::All(m) => m.clone(),
330 ImportSpec::Selective(m, _) => m.clone(),
331 ImportSpec::Hiding(m, _) => m.clone(),
332 ImportSpec::Renaming(m, _) => m.clone(),
333 };
334 deps.insert(mod_name);
335 }
336 deps
337 }
338 #[allow(dead_code)]
340 pub fn imports_module(&self, other: &str) -> bool {
341 self.get_dependencies().contains(other)
342 }
343 #[allow(dead_code)]
345 pub fn resolve_selective_import(&self, _module: &str, selected: &[String]) -> Vec<String> {
346 selected.to_vec()
347 }
348 #[allow(dead_code)]
350 pub fn get_hidden_names(&self, module: &str) -> Vec<String> {
351 for spec in &self.import_specs {
352 if let ImportSpec::Hiding(m, hidden) = spec {
353 if m == module {
354 return hidden.clone();
355 }
356 }
357 }
358 Vec::new()
359 }
360 #[allow(dead_code)]
362 pub fn create_nested_namespace(&mut self, name: String, parent: NamespaceScope) {
363 let mut nested = NamespaceScope {
364 name,
365 opened: Vec::new(),
366 aliases: HashMap::new(),
367 visibility: HashMap::new(),
368 parent: Some(Box::new(parent)),
369 };
370 if let Some(ref p) = nested.parent {
371 nested.aliases.extend(p.aliases.clone());
372 }
373 self.namespaces.push(nested);
374 }
375 #[allow(dead_code)]
377 pub fn lookup_in_namespaces(&self, name: &str) -> Option<String> {
378 for ns in self.namespaces.iter().rev() {
379 if let Some(full) = ns.aliases.get(name) {
380 return Some(full.clone());
381 }
382 let mut current_parent = &ns.parent;
383 while let Some(ref parent) = current_parent {
384 if let Some(full) = parent.aliases.get(name) {
385 return Some(full.clone());
386 }
387 current_parent = &parent.parent;
388 }
389 }
390 None
391 }
392 #[allow(dead_code)]
394 pub fn namespace_names(&self, ns_name: &str) -> Vec<String> {
395 for ns in &self.namespaces {
396 if ns.name == ns_name {
397 return ns.aliases.keys().cloned().collect();
398 }
399 }
400 Vec::new()
401 }
402 #[allow(dead_code)]
404 pub fn set_default_visibility(&mut self, visibility: Visibility) {
405 match visibility {
406 Visibility::Private => self.config.private_by_default = true,
407 _ => self.config.private_by_default = false,
408 }
409 }
410}
411#[derive(Debug, Clone, PartialEq)]
413pub enum ResolvedName {
414 Local(String),
416 Imported {
418 module: String,
420 name: String,
422 },
423 Ambiguous(Vec<String>),
425 NotFound,
427}
428#[allow(dead_code)]
430#[allow(missing_docs)]
431#[derive(Debug, Clone)]
432pub struct ModuleAttribute {
433 pub name: String,
435 pub arg: Option<String>,
437}
438impl ModuleAttribute {
439 #[allow(dead_code)]
441 pub fn new(name: &str) -> Self {
442 ModuleAttribute {
443 name: name.to_string(),
444 arg: None,
445 }
446 }
447 #[allow(dead_code)]
449 pub fn with_arg(mut self, arg: &str) -> Self {
450 self.arg = Some(arg.to_string());
451 self
452 }
453 #[allow(dead_code)]
455 pub fn format(&self) -> String {
456 if let Some(ref arg) = self.arg {
457 format!("@[{} {}]", self.name, arg)
458 } else {
459 format!("@[{}]", self.name)
460 }
461 }
462}
463#[derive(Debug, Clone, PartialEq)]
465pub enum ExportSpec {
466 All,
468 Selective(Vec<String>),
470}
471#[derive(Debug, Clone)]
473pub struct NameVisibility {
474 pub name: String,
476 pub visibility: Visibility,
478 pub from_module: Option<String>,
480}
481#[derive(Debug, Clone)]
483pub struct ModuleDep {
484 pub name: String,
486 pub deps: Vec<String>,
488}
489#[allow(dead_code)]
491#[allow(missing_docs)]
492#[derive(Debug, Clone)]
493pub struct ExportEntry {
494 pub module: String,
496 pub all: bool,
498 pub names: Vec<String>,
500}
501impl ExportEntry {
502 #[allow(dead_code)]
504 pub fn all(module: &str) -> Self {
505 ExportEntry {
506 module: module.to_string(),
507 all: true,
508 names: Vec::new(),
509 }
510 }
511 #[allow(dead_code)]
513 pub fn names(module: &str, names: Vec<&str>) -> Self {
514 ExportEntry {
515 module: module.to_string(),
516 all: false,
517 names: names.into_iter().map(|s| s.to_string()).collect(),
518 }
519 }
520}
521#[allow(dead_code)]
523#[allow(missing_docs)]
524#[derive(Debug, Clone, PartialEq, Eq)]
525pub struct ModuleVersion {
526 pub major: u32,
528 pub minor: u32,
530 pub patch: u32,
532}
533impl ModuleVersion {
534 #[allow(dead_code)]
536 pub fn new(major: u32, minor: u32, patch: u32) -> Self {
537 ModuleVersion {
538 major,
539 minor,
540 patch,
541 }
542 }
543 #[allow(dead_code)]
545 pub fn format(&self) -> String {
546 format!("{}.{}.{}", self.major, self.minor, self.patch)
547 }
548}
549#[derive(Debug, Clone)]
551pub struct ModuleConfig {
552 pub allow_circular: bool,
554 pub private_by_default: bool,
556 pub allow_shadowing: bool,
558}
559#[derive(Debug, Clone, PartialEq)]
561pub enum ImportSpec {
562 All(String),
564 Selective(String, Vec<String>),
566 Hiding(String, Vec<String>),
568 Renaming(String, Vec<(String, String)>),
570}
571#[derive(Debug, Clone, Copy, PartialEq, Eq)]
573pub enum Visibility {
574 Public,
576 Private,
578 Protected,
580}
581#[derive(Debug, Clone)]
583pub struct ScopedOpen {
584 pub module: String,
586 pub scope_var: Option<String>,
588}
589#[derive(Debug, Clone, PartialEq)]
591pub struct OpenDirective {
592 pub module: String,
594 pub scoped: bool,
596}
597#[allow(dead_code)]
599#[allow(missing_docs)]
600pub struct DepGraphExt {
601 pub edges: Vec<ModuleDepExt>,
603}
604impl DepGraphExt {
605 #[allow(dead_code)]
607 pub fn new() -> Self {
608 DepGraphExt { edges: Vec::new() }
609 }
610 #[allow(dead_code)]
612 pub fn add(&mut self, dep: ModuleDepExt) {
613 self.edges.push(dep);
614 }
615 #[allow(dead_code)]
617 pub fn direct_deps(&self, module: &str) -> Vec<&str> {
618 self.edges
619 .iter()
620 .filter(|e| e.from == module && e.direct)
621 .map(|e| e.to.as_str())
622 .collect()
623 }
624 #[allow(dead_code)]
626 pub fn dependents(&self, module: &str) -> Vec<&str> {
627 self.edges
628 .iter()
629 .filter(|e| e.to == module)
630 .map(|e| e.from.as_str())
631 .collect()
632 }
633 #[allow(dead_code)]
635 pub fn has_self_import(&self) -> bool {
636 self.edges.iter().any(|e| e.from == e.to)
637 }
638}
639#[allow(dead_code)]
641#[allow(missing_docs)]
642#[derive(Debug, Clone)]
643pub struct ModuleMetadata {
644 pub name: String,
646 pub version: Option<ModuleVersion>,
648 pub author: Option<String>,
650}
651impl ModuleMetadata {
652 #[allow(dead_code)]
654 pub fn new(name: &str) -> Self {
655 ModuleMetadata {
656 name: name.to_string(),
657 version: None,
658 author: None,
659 }
660 }
661 #[allow(dead_code)]
663 pub fn with_version(mut self, v: ModuleVersion) -> Self {
664 self.version = Some(v);
665 self
666 }
667 #[allow(dead_code)]
669 pub fn with_author(mut self, a: &str) -> Self {
670 self.author = Some(a.to_string());
671 self
672 }
673}
674#[allow(dead_code)]
676#[allow(missing_docs)]
677#[derive(Debug, Clone)]
678pub struct ModuleDepExt {
679 pub from: String,
681 pub to: String,
683 pub direct: bool,
685}
686impl ModuleDepExt {
687 #[allow(dead_code)]
689 pub fn direct(from: &str, to: &str) -> Self {
690 ModuleDepExt {
691 from: from.to_string(),
692 to: to.to_string(),
693 direct: true,
694 }
695 }
696 #[allow(dead_code)]
698 pub fn transitive(from: &str, to: &str) -> Self {
699 ModuleDepExt {
700 from: from.to_string(),
701 to: to.to_string(),
702 direct: false,
703 }
704 }
705}
706#[derive(Debug, Clone, PartialEq)]
708pub enum CycleDetectionResult {
709 NoCycles,
711 CycleFound(Vec<String>),
713 MultipleCycles(Vec<Vec<String>>),
715}
716pub struct ModuleRegistry {
718 modules: HashMap<String, Module>,
720}
721impl ModuleRegistry {
722 pub fn new() -> Self {
724 Self {
725 modules: HashMap::new(),
726 }
727 }
728 pub fn register(&mut self, module: Module) -> Result<(), String> {
730 let path = module.full_path();
731 match self.modules.entry(path.clone()) {
732 std::collections::hash_map::Entry::Occupied(_) => {
733 Err(format!("Module {} already registered", path))
734 }
735 std::collections::hash_map::Entry::Vacant(entry) => {
736 entry.insert(module);
737 Ok(())
738 }
739 }
740 }
741 pub fn get(&self, path: &str) -> Option<&Module> {
743 self.modules.get(path)
744 }
745 pub fn all_modules(&self) -> Vec<&Module> {
747 self.modules.values().collect()
748 }
749 pub fn contains(&self, path: &str) -> bool {
751 self.modules.contains_key(path)
752 }
753 #[allow(dead_code)]
755 pub fn get_mut(&mut self, path: &str) -> Option<&mut Module> {
756 self.modules.get_mut(path)
757 }
758 #[allow(dead_code)]
760 pub fn unregister(&mut self, path: &str) -> Option<Module> {
761 self.modules.remove(path)
762 }
763 #[allow(dead_code)]
768 pub fn resolve(&self, from: &str, name: &str) -> ResolvedName {
769 let module = match self.modules.get(from) {
770 Some(m) => m,
771 None => return ResolvedName::NotFound,
772 };
773 let local = module.resolve_name(name);
774 if matches!(local, ResolvedName::Local(_)) {
775 return local;
776 }
777 for spec in &module.import_specs {
778 match spec {
779 ImportSpec::All(mod_name) => {
780 if let Some(target) = self.modules.get(mod_name) {
781 let exported = target.exported_names();
782 if exported.contains(&name.to_string()) {
783 return ResolvedName::Imported {
784 module: mod_name.clone(),
785 name: name.to_string(),
786 };
787 }
788 }
789 }
790 ImportSpec::Selective(mod_name, selected) => {
791 if selected.contains(&name.to_string()) {
792 if let Some(target) = self.modules.get(mod_name) {
793 let exported = target.exported_names();
794 if exported.contains(&name.to_string()) {
795 return ResolvedName::Imported {
796 module: mod_name.clone(),
797 name: name.to_string(),
798 };
799 }
800 }
801 }
802 }
803 ImportSpec::Hiding(mod_name, hidden) => {
804 if !hidden.contains(&name.to_string()) {
805 if let Some(target) = self.modules.get(mod_name) {
806 let exported = target.exported_names();
807 if exported.contains(&name.to_string()) {
808 return ResolvedName::Imported {
809 module: mod_name.clone(),
810 name: name.to_string(),
811 };
812 }
813 }
814 }
815 }
816 ImportSpec::Renaming(mod_name, renamings) => {
817 for (from_name, to_name) in renamings {
818 if to_name == name {
819 return ResolvedName::Imported {
820 module: mod_name.clone(),
821 name: from_name.clone(),
822 };
823 }
824 }
825 }
826 }
827 }
828 for imp in &module.imports {
829 if let Some(target) = self.modules.get(imp) {
830 let exported = target.exported_names();
831 if exported.contains(&name.to_string()) {
832 return ResolvedName::Imported {
833 module: imp.clone(),
834 name: name.to_string(),
835 };
836 }
837 }
838 }
839 ResolvedName::NotFound
840 }
841 #[allow(dead_code)]
846 pub fn dependency_order(&self) -> Result<Vec<String>, Vec<String>> {
847 let deps = self.build_dep_graph();
848 let mut in_degree: HashMap<String, usize> = HashMap::new();
849 let mut adj: HashMap<String, Vec<String>> = HashMap::new();
850 for dep in &deps {
851 in_degree.entry(dep.name.clone()).or_insert(0);
852 adj.entry(dep.name.clone()).or_default();
853 for d in &dep.deps {
854 adj.entry(d.clone()).or_default();
855 in_degree.entry(d.clone()).or_insert(0);
856 *in_degree.entry(dep.name.clone()).or_insert(0) += 1;
857 }
858 }
859 let mut queue: Vec<String> = in_degree
860 .iter()
861 .filter(|(_, °)| deg == 0)
862 .map(|(name, _)| name.clone())
863 .collect();
864 queue.sort();
865 let mut result = Vec::new();
866 while let Some(node) = queue.pop() {
867 result.push(node.clone());
868 if let Some(neighbors) = adj.get(&node) {
869 for _neighbor in neighbors {}
870 let _ = neighbors;
871 }
872 }
873 let mut adj2: HashMap<String, Vec<String>> = HashMap::new();
874 let mut in_deg2: HashMap<String, usize> = HashMap::new();
875 for dep in &deps {
876 in_deg2.entry(dep.name.clone()).or_insert(0);
877 adj2.entry(dep.name.clone()).or_default();
878 for d in &dep.deps {
879 adj2.entry(d.clone()).or_default();
880 in_deg2.entry(d.clone()).or_insert(0);
881 adj2.entry(d.clone()).or_default().push(dep.name.clone());
882 *in_deg2.entry(dep.name.clone()).or_insert(0) += 1;
883 }
884 }
885 let mut queue2: Vec<String> = in_deg2
886 .iter()
887 .filter(|(_, °)| deg == 0)
888 .map(|(name, _)| name.clone())
889 .collect();
890 queue2.sort();
891 let mut sorted = Vec::new();
892 while let Some(node) = queue2.pop() {
893 sorted.push(node.clone());
894 if let Some(neighbors) = adj2.get(&node) {
895 for neighbor in neighbors.clone() {
896 if let Some(deg) = in_deg2.get_mut(&neighbor) {
897 *deg -= 1;
898 if *deg == 0 {
899 queue2.push(neighbor);
900 queue2.sort();
901 }
902 }
903 }
904 }
905 }
906 let total_nodes = in_deg2.len();
907 if sorted.len() == total_nodes {
908 Ok(sorted)
909 } else {
910 let cycle: Vec<String> = in_deg2
911 .iter()
912 .filter(|(_, °)| deg > 0)
913 .map(|(name, _)| name.clone())
914 .collect();
915 Err(cycle)
916 }
917 }
918 #[allow(dead_code)]
922 pub fn detect_cycles(&self) -> Vec<Vec<String>> {
923 let deps = self.build_dep_graph();
924 let mut visited: HashMap<String, u8> = HashMap::new();
925 let mut path: Vec<String> = Vec::new();
926 let mut cycles: Vec<Vec<String>> = Vec::new();
927 let dep_map: HashMap<String, Vec<String>> = deps
928 .iter()
929 .map(|d| (d.name.clone(), d.deps.clone()))
930 .collect();
931 for dep in &deps {
932 if visited.get(&dep.name).copied().unwrap_or(0) == 0 {
933 Self::dfs_cycle(&dep.name, &dep_map, &mut visited, &mut path, &mut cycles);
934 }
935 }
936 cycles
937 }
938 fn dfs_cycle(
940 node: &str,
941 adj: &HashMap<String, Vec<String>>,
942 visited: &mut HashMap<String, u8>,
943 path: &mut Vec<String>,
944 cycles: &mut Vec<Vec<String>>,
945 ) {
946 visited.insert(node.to_string(), 1);
947 path.push(node.to_string());
948 if let Some(neighbors) = adj.get(node) {
949 for neighbor in neighbors {
950 let state = visited.get(neighbor).copied().unwrap_or(0);
951 if state == 1 {
952 if let Some(pos) = path.iter().position(|n| n == neighbor) {
953 let cycle: Vec<String> = path[pos..].to_vec();
954 cycles.push(cycle);
955 }
956 } else if state == 0 {
957 Self::dfs_cycle(neighbor, adj, visited, path, cycles);
958 }
959 }
960 }
961 path.pop();
962 visited.insert(node.to_string(), 2);
963 }
964 #[allow(dead_code)]
966 pub fn transitive_deps(&self, module: &str) -> Vec<String> {
967 let dep_map: HashMap<String, Vec<String>> = self
968 .build_dep_graph()
969 .into_iter()
970 .map(|d| (d.name, d.deps))
971 .collect();
972 let mut visited = Vec::new();
973 let mut stack = Vec::new();
974 if let Some(direct) = dep_map.get(module) {
975 stack.extend(direct.clone());
976 }
977 while let Some(dep) = stack.pop() {
978 if visited.contains(&dep) {
979 continue;
980 }
981 visited.push(dep.clone());
982 if let Some(transitive) = dep_map.get(&dep) {
983 for t in transitive {
984 if !visited.contains(t) {
985 stack.push(t.clone());
986 }
987 }
988 }
989 }
990 visited.sort();
991 visited
992 }
993 fn build_dep_graph(&self) -> Vec<ModuleDep> {
995 self.modules
996 .values()
997 .map(|m| {
998 let mut deps = m.imports.clone();
999 for spec in &m.import_specs {
1000 let mod_name = match spec {
1001 ImportSpec::All(n) => n.clone(),
1002 ImportSpec::Selective(n, _) => n.clone(),
1003 ImportSpec::Hiding(n, _) => n.clone(),
1004 ImportSpec::Renaming(n, _) => n.clone(),
1005 };
1006 if !deps.contains(&mod_name) {
1007 deps.push(mod_name);
1008 }
1009 }
1010 ModuleDep {
1011 name: m.full_path(),
1012 deps,
1013 }
1014 })
1015 .collect()
1016 }
1017 #[allow(dead_code)]
1019 pub fn detect_mutual_imports(&self) -> Vec<(String, String)> {
1020 let mut mutual = Vec::new();
1021 for module in self.modules.values() {
1022 for dep in &module.imports {
1023 if let Some(dep_module) = self.modules.get(dep) {
1024 if dep_module.imports.contains(&module.full_path()) {
1025 let pair = (module.full_path(), dep.clone());
1026 if !mutual.iter().any(|(a, b)| {
1027 (a == &pair.0 && b == &pair.1) || (a == &pair.1 && b == &pair.0)
1028 }) {
1029 mutual.push(pair);
1030 }
1031 }
1032 }
1033 }
1034 }
1035 mutual
1036 }
1037 #[allow(dead_code)]
1039 pub fn are_mutually_dependent(&self, a: &str, b: &str) -> bool {
1040 if let (Some(mod_a), Some(mod_b)) = (self.modules.get(a), self.modules.get(b)) {
1041 (mod_a.imports.contains(&b.to_string()) && mod_b.imports.contains(&a.to_string()))
1042 || (mod_a.imports_module(b) && mod_b.imports_module(a))
1043 } else {
1044 false
1045 }
1046 }
1047 #[allow(dead_code)]
1049 pub fn get_dependents(&self, module_name: &str) -> Vec<String> {
1050 let mut dependents = Vec::new();
1051 for module in self.modules.values() {
1052 if module.imports_module(module_name) {
1053 dependents.push(module.full_path());
1054 }
1055 }
1056 dependents.sort();
1057 dependents
1058 }
1059 #[allow(dead_code)]
1061 pub fn get_all_dependencies(&self, module_name: &str) -> Vec<String> {
1062 let mut all_deps = HashSet::new();
1063 let mut stack = vec![module_name.to_string()];
1064 let mut visited = HashSet::new();
1065 while let Some(current) = stack.pop() {
1066 if visited.contains(¤t) {
1067 continue;
1068 }
1069 visited.insert(current.clone());
1070 if let Some(module) = self.modules.get(¤t) {
1071 for dep in module.get_dependencies() {
1072 if !visited.contains(&dep) {
1073 all_deps.insert(dep.clone());
1074 stack.push(dep);
1075 }
1076 }
1077 }
1078 }
1079 let mut result: Vec<_> = all_deps.into_iter().collect();
1080 result.sort();
1081 result
1082 }
1083 #[allow(dead_code)]
1085 pub fn verify_consistency(&self) -> Result<(), Vec<String>> {
1086 let mut errors = Vec::new();
1087 for module in self.modules.values() {
1088 for imported in &module.imports {
1089 if !self.modules.contains_key(imported) {
1090 errors.push(format!(
1091 "Module {} imports non-existent module {}",
1092 module.full_path(),
1093 imported
1094 ));
1095 }
1096 }
1097 }
1098 if errors.is_empty() {
1099 Ok(())
1100 } else {
1101 Err(errors)
1102 }
1103 }
1104 #[allow(dead_code)]
1106 pub fn get_reverse_dependencies(&self, module_name: &str) -> Vec<String> {
1107 let mut affected = HashSet::new();
1108 let mut stack = vec![module_name.to_string()];
1109 let mut visited = HashSet::new();
1110 while let Some(current) = stack.pop() {
1111 if visited.contains(¤t) {
1112 continue;
1113 }
1114 visited.insert(current.clone());
1115 for dependent in self.get_dependents(¤t) {
1116 if !visited.contains(&dependent) {
1117 affected.insert(dependent.clone());
1118 stack.push(dependent);
1119 }
1120 }
1121 }
1122 let mut result: Vec<_> = affected.into_iter().collect();
1123 result.sort();
1124 result
1125 }
1126 #[allow(dead_code)]
1128 pub fn reachable_from(&self, module_name: &str) -> Vec<String> {
1129 let mut reachable = Vec::new();
1130 let mut visited = HashSet::new();
1131 let mut stack = vec![module_name.to_string()];
1132 while let Some(current) = stack.pop() {
1133 if visited.contains(¤t) {
1134 continue;
1135 }
1136 visited.insert(current.clone());
1137 if let Some(module) = self.modules.get(¤t) {
1138 for dep in module.get_dependencies() {
1139 if !visited.contains(&dep) {
1140 reachable.push(dep.clone());
1141 stack.push(dep);
1142 }
1143 }
1144 }
1145 }
1146 reachable.sort();
1147 reachable
1148 }
1149 #[allow(dead_code)]
1151 pub fn get_sccs(&self) -> Vec<Vec<String>> {
1152 let mut sccs = Vec::new();
1153 let deps = self.build_dep_graph();
1154 let mut visited: HashMap<String, bool> = HashMap::new();
1155 let mut rec_stack: HashMap<String, bool> = HashMap::new();
1156 let mut current_scc = Vec::new();
1157 for dep in &deps {
1158 if !visited.contains_key(&dep.name) {
1159 self.tarjan_visit(
1160 &dep.name,
1161 &deps,
1162 &mut visited,
1163 &mut rec_stack,
1164 &mut current_scc,
1165 &mut sccs,
1166 );
1167 }
1168 }
1169 sccs
1170 }
1171 fn tarjan_visit(
1173 &self,
1174 node: &str,
1175 _graph: &[ModuleDep],
1176 visited: &mut HashMap<String, bool>,
1177 rec_stack: &mut HashMap<String, bool>,
1178 _current_scc: &mut Vec<String>,
1179 sccs: &mut Vec<Vec<String>>,
1180 ) {
1181 visited.insert(node.to_string(), true);
1182 rec_stack.insert(node.to_string(), true);
1183 if let Some(module) = self.modules.get(node) {
1184 for dep in &module.imports {
1185 if !visited.get(dep).copied().unwrap_or(false) {
1186 self.tarjan_visit(dep, _graph, visited, rec_stack, _current_scc, sccs);
1187 } else if rec_stack.get(dep).copied().unwrap_or(false) {
1188 sccs.push(vec![node.to_string(), dep.clone()]);
1189 }
1190 }
1191 }
1192 rec_stack.insert(node.to_string(), false);
1193 }
1194 #[allow(dead_code)]
1196 pub fn get_statistics(&self) -> HashMap<String, usize> {
1197 let mut stats = HashMap::new();
1198 let mut total_imports = 0;
1199 let mut total_opens = 0;
1200 for module in self.modules.values() {
1201 total_imports += module.imports.len();
1202 total_opens += module.opens.len();
1203 }
1204 stats.insert("modules".to_string(), self.modules.len());
1205 stats.insert("total_imports".to_string(), total_imports);
1206 stats.insert("total_opens".to_string(), total_opens);
1207 stats
1208 }
1209 #[allow(dead_code)]
1211 pub fn get_public_interface(&self, module_name: &str) -> Vec<String> {
1212 if let Some(module) = self.modules.get(module_name) {
1213 module
1214 .exported_names()
1215 .into_iter()
1216 .filter(|name| module.get_visibility(name) == Visibility::Public)
1217 .collect()
1218 } else {
1219 Vec::new()
1220 }
1221 }
1222 #[allow(dead_code)]
1224 pub fn is_name_accessible(&self, name: &str, from_module: &str, to_module: &str) -> bool {
1225 if let Some(target) = self.modules.get(to_module) {
1226 let exported = target.exported_names();
1227 if exported.contains(&name.to_string()) {
1228 return target.is_accessible(name, from_module == to_module);
1229 }
1230 }
1231 false
1232 }
1233}
1234#[derive(Debug, Clone)]
1236pub struct NamespaceScope {
1237 pub name: String,
1239 pub opened: Vec<String>,
1241 pub aliases: HashMap<String, String>,
1243 pub visibility: HashMap<String, Visibility>,
1245 pub parent: Option<Box<NamespaceScope>>,
1247}