use std::collections::BTreeMap;
use weaveffi_ir::ir::{Api, Function, Module, TypeRef};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum TypeKind {
Record,
Enum,
RichEnum,
Interface,
}
impl TypeKind {
fn type_ref(self, name: String) -> TypeRef {
match self {
TypeKind::Record => TypeRef::Record(name),
TypeKind::Enum => TypeRef::Enum(name),
TypeKind::RichEnum => TypeRef::RichEnum(name),
TypeKind::Interface => TypeRef::Interface(name),
}
}
}
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::Record));
}
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 i in &module.interfaces {
out.entry(i.name.clone())
.or_insert((path.clone(), TypeKind::Interface));
}
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 ctx = ResolveCtx {
current_module: &module_path,
global_types,
};
fn resolve_callable(f: &mut Function, ctx: &ResolveCtx<'_>) {
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 f in &mut module.functions {
resolve_callable(f, &ctx);
}
for i in &mut module.interfaces {
for c in &mut i.constructors {
resolve_callable(c, &ctx);
}
for m in &mut i.methods {
resolve_callable(m, &ctx);
}
for s in &mut i.statics {
resolve_callable(s, &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 cb in &mut module.callbacks {
for p in &mut cb.params {
resolve_single_type_ref(&mut p.ty, &ctx);
}
}
for child in &mut module.modules {
resolve_module_type_refs(child, &module_path, global_types);
}
}
struct ResolveCtx<'a> {
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::Named(name) => {
if let Some((mod_name, kind)) = ctx.global_types.get(name.as_str()) {
let resolved_name = if mod_name == ctx.current_module {
std::mem::take(name)
} else {
format!("{mod_name}.{name}")
};
*ty = kind.type_ref(resolved_name);
}
}
TypeRef::TypedHandle(name) => {
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)
|| module.interfaces.iter().any(|i| i.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))
}