use super::*;
#[derive(Debug)]
pub enum Item<'a> {
Type(TypeDef<'a>),
Fn(MethodDef<'a>),
Const(Field<'a>),
}
type HashType<'a> = HashMap<&'a str, HashMap<&'a str, Vec<Item<'a>>>>;
pub struct ItemIndex<'a>(HashType<'a>);
impl<'a> core::ops::Deref for ItemIndex<'a> {
type Target = HashType<'a>;
fn deref(&self) -> &HashType<'a> {
&self.0
}
}
impl<'a> ItemIndex<'a> {
pub fn new(index: &'a TypeIndex) -> Self {
let mut members: HashType = HashMap::new();
for (namespace, name, ty) in index.iter() {
let apis = !ty.flags().contains(TypeAttributes::WindowsRuntime)
&& ty.category() == TypeCategory::Class
&& name == "Apis";
if apis {
for method in ty.methods() {
insert(&mut members, namespace, method.name(), Item::Fn(method));
}
for field in ty.fields() {
insert(&mut members, namespace, field.name(), Item::Const(field));
}
} else {
insert(&mut members, namespace, name, Item::Type(ty));
}
if !ty.flags().contains(TypeAttributes::WindowsRuntime) {
match ty.category() {
TypeCategory::Enum if !ty.has_attribute("ScopedEnumAttribute") => {
for field in ty.fields() {
if field.flags().contains(FieldAttributes::Literal) {
insert(&mut members, namespace, field.name(), Item::Const(field));
}
}
}
_ => {}
}
}
}
Self(members)
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &str, &Item<'_>)> + '_ {
self.0
.iter()
.flat_map(|(namespace, items)| {
items
.iter()
.map(move |(name, items)| (namespace, name, items))
})
.flat_map(|(namespace, name, items)| {
items.iter().map(move |item| (*namespace, *name, item))
})
}
pub fn items(&self) -> impl Iterator<Item = &Item<'_>> + '_ {
self.0.values().flat_map(|items| items.values()).flatten()
}
pub fn namespace_items(&self, namespace: &str) -> impl Iterator<Item = (&str, &Item<'_>)> + '_ {
self.0
.get(namespace)
.into_iter()
.flatten()
.flat_map(|(name, items)| items.iter().map(move |item| (*name, item)))
}
pub fn get(&self, namespace: &str, name: &str) -> impl Iterator<Item = &Item<'_>> + '_ {
self.0
.get(namespace)
.and_then(|items| items.get(name))
.into_iter()
.flatten()
}
#[track_caller]
pub fn expect(&self, namespace: &str, name: &str) -> &Item<'_> {
let mut iter = self.get(namespace, name);
if let Some(item) = iter.next() {
if iter.next().is_none() {
item
} else {
panic!("more than one type found: {namespace}.{name}");
}
} else {
panic!("type not found: {namespace}.{name}")
}
}
}
fn insert<'a>(members: &mut HashType<'a>, namespace: &'a str, name: &'a str, member: Item<'a>) {
members
.entry(namespace)
.or_default()
.entry(name)
.or_default()
.push(member);
}