use std::collections::{HashMap, HashSet};
use crate::core::ir::{EnumDef, TypeDef};
use crate::e2e::codegen::call_ir::named_type;
use super::parse::parse_path;
use super::types::{IrEnumMap, PathSegment};
pub(super) fn build_ir_enum_map(type_defs: &[TypeDef], enums: &[EnumDef]) -> IrEnumMap {
let enum_names: HashSet<&str> = enums.iter().map(|e| e.name.as_str()).collect();
let struct_names: HashSet<&str> = type_defs.iter().map(|t| t.name.as_str()).collect();
let mut field_types: HashMap<String, HashMap<String, String>> = HashMap::new();
let mut enum_fields: HashMap<String, HashSet<String>> = HashMap::new();
for type_def in type_defs {
for field in &type_def.fields {
let Some(named) = named_type(&field.ty) else {
continue;
};
if enum_names.contains(named) {
enum_fields
.entry(type_def.name.clone())
.or_default()
.insert(field.name.clone());
} else if struct_names.contains(named) {
field_types
.entry(type_def.name.clone())
.or_default()
.insert(field.name.clone(), named.to_string());
}
}
}
IrEnumMap {
field_types,
enum_fields,
root_type: None,
}
}
fn segment_name(segment: &PathSegment) -> Option<&str> {
match segment {
PathSegment::Field(name) | PathSegment::ArrayField { name, .. } => Some(name),
PathSegment::MapAccess { field, .. } => Some(field),
PathSegment::Length => None,
}
}
pub(super) fn is_enum_path(map: &IrEnumMap, path: &str) -> bool {
let Some(root) = map.root_type.as_deref() else {
return false;
};
let segments = parse_path(path);
let Some((last, prefix)) = segments.split_last() else {
return false;
};
let mut owner = root;
for segment in prefix {
let Some(name) = segment_name(segment) else {
return false;
};
match map.field_types.get(owner).and_then(|fields| fields.get(name)) {
Some(next) => owner = next.as_str(),
None => return false,
}
}
let Some(name) = segment_name(last) else {
return false;
};
map.enum_fields.get(owner).is_some_and(|fields| fields.contains(name))
}