use std::collections::{BTreeMap, BTreeSet};
use weaveffi_ir::ir::{Api, Module, TypeRef};
pub fn resolve_type_refs(api: &mut Api) {
let mut global_types: BTreeMap<String, (String, bool)> = BTreeMap::new();
for module in &api.modules {
index_module_types(module, "", &mut global_types);
}
for module in &mut api.modules {
resolve_module_type_refs(module, "", &global_types);
}
}
fn index_module_types(
module: &Module,
parent_path: &str,
out: &mut BTreeMap<String, (String, bool)>,
) {
let path = join_module_path(parent_path, &module.name);
for s in &module.structs {
out.entry(s.name.clone()).or_insert((path.clone(), false));
}
for e in &module.enums {
out.entry(e.name.clone()).or_insert((path.clone(), true));
}
for child in &module.modules {
index_module_types(child, &path, out);
}
}
fn resolve_module_type_refs(
module: &mut Module,
parent_path: &str,
global_types: &BTreeMap<String, (String, bool)>,
) {
let module_path = join_module_path(parent_path, &module.name);
let local_enum_names: BTreeSet<String> = module.enums.iter().map(|e| e.name.clone()).collect();
let local_struct_names: BTreeSet<String> =
module.structs.iter().map(|s| s.name.clone()).collect();
for f in &mut module.functions {
for p in &mut f.params {
resolve_single_type_ref(
&mut p.ty,
&local_enum_names,
&local_struct_names,
&module_path,
global_types,
);
}
if let Some(ret) = &mut f.returns {
resolve_single_type_ref(
ret,
&local_enum_names,
&local_struct_names,
&module_path,
global_types,
);
}
}
for s in &mut module.structs {
for field in &mut s.fields {
resolve_single_type_ref(
&mut field.ty,
&local_enum_names,
&local_struct_names,
&module_path,
global_types,
);
}
}
for child in &mut module.modules {
resolve_module_type_refs(child, &module_path, global_types);
}
}
fn join_module_path(parent_path: &str, name: &str) -> String {
if parent_path.is_empty() {
name.to_string()
} else {
format!("{parent_path}.{name}")
}
}
fn resolve_single_type_ref(
ty: &mut TypeRef,
local_enum_names: &BTreeSet<String>,
local_struct_names: &BTreeSet<String>,
current_module: &str,
global_types: &BTreeMap<String, (String, bool)>,
) {
match ty {
TypeRef::Struct(name) if local_enum_names.contains(name.as_str()) => {
let name = std::mem::take(name);
*ty = TypeRef::Enum(name);
}
TypeRef::Struct(name) if !local_struct_names.contains(name.as_str()) => {
if let Some((mod_name, is_enum)) = global_types.get(name.as_str()) {
if mod_name != current_module {
let qualified = format!("{mod_name}.{name}");
if *is_enum {
*ty = TypeRef::Enum(qualified);
} else {
*name = qualified;
}
}
}
}
TypeRef::TypedHandle(name) if !local_struct_names.contains(name.as_str()) => {
if let Some((mod_name, _is_enum)) = global_types.get(name.as_str()) {
if mod_name != current_module {
*name = format!("{mod_name}.{name}");
}
}
}
TypeRef::Optional(inner) | TypeRef::List(inner) | TypeRef::Iterator(inner) => {
resolve_single_type_ref(
inner,
local_enum_names,
local_struct_names,
current_module,
global_types,
);
}
TypeRef::Map(k, v) => {
resolve_single_type_ref(
k,
local_enum_names,
local_struct_names,
current_module,
global_types,
);
resolve_single_type_ref(
v,
local_enum_names,
local_struct_names,
current_module,
global_types,
);
}
_ => {}
}
}
pub fn find_type_in_api(api: &Api, name: &str) -> Option<(String, bool)> {
fn search(module: &Module, parent_path: &str, name: &str) -> Option<(String, bool)> {
let path = join_module_path(parent_path, &module.name);
if module.structs.iter().any(|s| s.name == name) {
return Some((path, false));
}
if module.enums.iter().any(|e| e.name == name) {
return Some((path, true));
}
module
.modules
.iter()
.find_map(|child| search(child, &path, name))
}
api.modules
.iter()
.find_map(|module| search(module, "", name))
}