use std::collections::{BTreeMap, BTreeSet};
use weaveffi_ir::ir::{Api, Module, TypeRef};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum TypeKind {
Struct,
Enum,
RichEnum,
}
pub fn resolve_type_refs(api: &mut Api) {
let mut global_types: BTreeMap<String, (String, TypeKind)> = 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, TypeKind)>,
) {
let path = join_module_path(parent_path, &module.name);
for s in &module.structs {
out.entry(s.name.clone())
.or_insert((path.clone(), TypeKind::Struct));
}
for e in &module.enums {
let kind = if e.is_rich() {
TypeKind::RichEnum
} else {
TypeKind::Enum
};
out.entry(e.name.clone()).or_insert((path.clone(), kind));
}
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, TypeKind)>,
) {
let module_path = join_module_path(parent_path, &module.name);
let local_enum_names: BTreeSet<String> = module
.enums
.iter()
.filter(|e| !e.is_rich())
.map(|e| e.name.clone())
.collect();
let local_struct_names: BTreeSet<String> =
module.structs.iter().map(|s| s.name.clone()).collect();
let ctx = ResolveCtx {
local_enum_names: &local_enum_names,
local_struct_names: &local_struct_names,
current_module: &module_path,
global_types,
};
for f in &mut module.functions {
for p in &mut f.params {
resolve_single_type_ref(&mut p.ty, &ctx);
}
if let Some(ret) = &mut f.returns {
resolve_single_type_ref(ret, &ctx);
}
}
for s in &mut module.structs {
for field in &mut s.fields {
resolve_single_type_ref(&mut field.ty, &ctx);
}
}
for e in &mut module.enums {
for v in &mut e.variants {
for field in &mut v.fields {
resolve_single_type_ref(&mut field.ty, &ctx);
}
}
}
for child in &mut module.modules {
resolve_module_type_refs(child, &module_path, global_types);
}
}
struct ResolveCtx<'a> {
local_enum_names: &'a BTreeSet<String>,
local_struct_names: &'a BTreeSet<String>,
current_module: &'a str,
global_types: &'a BTreeMap<String, (String, TypeKind)>,
}
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, ctx: &ResolveCtx<'_>) {
match ty {
TypeRef::Struct(name) if ctx.local_enum_names.contains(name.as_str()) => {
let name = std::mem::take(name);
*ty = TypeRef::Enum(name);
}
TypeRef::Struct(name) if !ctx.local_struct_names.contains(name.as_str()) => {
if let Some((mod_name, kind)) = ctx.global_types.get(name.as_str()) {
if mod_name != ctx.current_module {
let qualified = format!("{mod_name}.{name}");
match kind {
TypeKind::Enum => *ty = TypeRef::Enum(qualified),
TypeKind::Struct | TypeKind::RichEnum => *name = qualified,
}
}
}
}
TypeRef::TypedHandle(name) if !ctx.local_struct_names.contains(name.as_str()) => {
if let Some((mod_name, _kind)) = ctx.global_types.get(name.as_str()) {
if mod_name != ctx.current_module {
*name = format!("{mod_name}.{name}");
}
}
}
TypeRef::Optional(inner) | TypeRef::List(inner) | TypeRef::Iterator(inner) => {
resolve_single_type_ref(inner, ctx);
}
TypeRef::Map(k, v) => {
resolve_single_type_ref(k, ctx);
resolve_single_type_ref(v, ctx);
}
_ => {}
}
}
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))
}