extern crate alloc;
use alloc::sync::Arc;
use alloc::vec::Vec;
use std::collections::HashMap;
use crate::records::file_resolver::FileResolver;
use crate::records::require_cycle::RequireCycle;
use crate::records::source_node::SourceNode;
use crate::type_aliases::module_name_file_resolver::ModuleName;
use luaur_common::macros::luau_assert::LUAU_ASSERT;
use luaur_common::records::dense_hash_set::DenseHashSet;
pub fn get_require_cycles(
_resolver: &FileResolver,
source_nodes: &HashMap<ModuleName, Arc<SourceNode>>,
start: &SourceNode,
) -> Vec<RequireCycle> {
let mut result: Vec<RequireCycle> = Vec::new();
let mut seen: DenseHashSet<*const SourceNode> = DenseHashSet::new(core::ptr::null());
let mut stack: Vec<*const SourceNode> = Vec::new();
let mut path: Vec<*const SourceNode> = Vec::new();
for (dep_name, dep_location) in &start.require_locations {
let mut cycle: Vec<ModuleName> = Vec::new();
let dit = source_nodes.get(dep_name);
if dit.is_none() {
continue;
}
stack.push(dit.unwrap().as_ref() as *const SourceNode);
while let Some(top_ptr) = stack.pop() {
let top: *const SourceNode = top_ptr;
if top.is_null() {
LUAU_ASSERT!(!path.is_empty());
let last_path = unsafe { *path.last().unwrap() };
path.pop();
if last_path == (start as *const SourceNode) {
for &node in &path {
cycle.push(unsafe { (*node).name.clone() });
}
cycle.push(unsafe { (*last_path).name.clone() });
break;
}
} else if !seen.contains(&top) {
seen.insert(top);
path.push(top);
stack.push(core::ptr::null());
let require_locations = unsafe { &(*top).require_locations };
for i in (1..=require_locations.len()).rev() {
let req_name = &require_locations[i - 1].0;
let rit = source_nodes.get(req_name);
if let Some(rit_node) = rit {
stack.push(rit_node.as_ref() as *const SourceNode);
}
}
}
}
path.clear();
stack.clear();
if !cycle.is_empty() {
result.push(RequireCycle {
location: dep_location.clone(),
path: cycle,
});
seen.clear();
}
}
result
}