use super::*;
pub struct Index {
files: Vec<File>,
types: HashMap<String, HashMap<String, Vec<(usize, usize)>>>,
items: HashMap<String, HashMap<String, Vec<RawItem>>>,
nested: HashMap<(usize, usize), Vec<usize>>,
architecture: i32,
}
#[derive(Copy, Clone)]
enum RawItem {
Type(usize, usize), Fn(usize, usize), Const(usize, usize), }
#[derive(Copy, Clone, Debug)]
pub enum Item<'a> {
Type(TypeDef<'a>),
Fn(MethodDef<'a>),
Const(Field<'a>),
}
impl Index {
#[must_use]
pub fn read<P: AsRef<std::path::Path>>(path: P) -> Option<Self> {
Some(Self::new(vec![File::read(path)?]))
}
#[must_use]
pub fn new(files: Vec<File>) -> Self {
Self::new_for_architecture(files, 0)
}
#[must_use]
pub fn new_for_architecture(files: Vec<File>, architecture: i32) -> Self {
let mut types: HashMap<String, HashMap<String, Vec<(usize, usize)>>> = HashMap::new();
let mut nested: HashMap<(usize, usize), Vec<usize>> = HashMap::new();
for (file_pos, file) in files.iter().enumerate() {
for def_pos in file.TypeDef() {
let namespace = file.str(def_pos, TypeDef::TABLE, 2);
if namespace.is_empty() {
continue;
}
let name = file.str(def_pos, TypeDef::TABLE, 1);
types
.entry(namespace.to_string())
.or_default()
.entry(trim_tick(name).to_string())
.or_default()
.push((file_pos, def_pos));
}
for map in file.NestedClass() {
let inner = file.usize(map, NestedClass::TABLE, 0) - 1;
let outer = file.usize(map, NestedClass::TABLE, 1) - 1;
nested.entry((file_pos, outer)).or_default().push(inner);
}
}
let mut index = Self {
files,
types,
items: HashMap::new(),
nested,
architecture,
};
let mut items: HashMap<String, HashMap<String, Vec<RawItem>>> = 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()
.filter(|method| index.supports_architecture(*method))
{
items
.entry(namespace.to_string())
.or_default()
.entry(method.name().to_string())
.or_default()
.push(RawItem::Fn(ty.0.file, method.0.pos));
}
for field in ty
.fields()
.filter(|field| index.supports_architecture(*field))
{
items
.entry(namespace.to_string())
.or_default()
.entry(field.name().to_string())
.or_default()
.push(RawItem::Const(ty.0.file, field.0.pos));
}
} else {
items
.entry(namespace.to_string())
.or_default()
.entry(name.to_string())
.or_default()
.push(RawItem::Type(ty.0.file, ty.0.pos));
}
}
index.items = items;
index
}
#[must_use]
pub fn leak(self) -> &'static Self {
Box::leak(Box::new(self))
}
#[must_use]
pub fn read_static<P: AsRef<std::path::Path>>(path: P) -> Option<&'static Self> {
Some(Self::new(vec![File::read(path)?]).leak())
}
pub(crate) fn files(&self, pos: usize) -> &File {
&self.files[pos]
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &str, TypeDef<'_>)> + '_ {
self.types
.iter()
.flat_map(|(namespace, types)| {
types
.iter()
.map(move |(name, types)| (namespace.as_str(), name.as_str(), types))
})
.flat_map(|(namespace, name, types)| types.iter().map(move |ty| (namespace, name, ty)))
.map(|(namespace, name, (file, pos))| {
(namespace, name, TypeDef(Row::new(self, *file, *pos)))
})
.filter(|(_, _, def)| self.supports_architecture(*def))
}
pub fn types(&self) -> impl Iterator<Item = TypeDef<'_>> + '_ {
self.types
.values()
.flat_map(|types| types.values())
.flatten()
.map(|(file, pos)| TypeDef(Row::new(self, *file, *pos)))
.filter(|def| self.supports_architecture(*def))
}
pub fn get(&self, namespace: &str, name: &str) -> impl Iterator<Item = TypeDef<'_>> + '_ {
self.types
.get(namespace)
.and_then(|types| types.get(name))
.into_iter()
.flatten()
.map(|(file, pos)| TypeDef(Row::new(self, *file, *pos)))
.filter(|def| self.supports_architecture(*def))
}
pub fn contains_namespace(&self, namespace: &str) -> bool {
self.types.get(namespace).is_some_and(|types| {
types
.values()
.flatten()
.any(|(file, pos)| self.supports_architecture(TypeDef(Row::new(self, *file, *pos))))
})
}
pub fn contains(&self, namespace: &str, name: &str) -> bool {
self.get(namespace, name).next().is_some()
}
pub fn assembly_name(&self, namespace: &str, name: &str) -> Option<&str> {
self.types
.get(namespace)
.and_then(|types| types.get(name))
.and_then(|types| {
types.iter().find(|(file, pos)| {
self.supports_architecture(TypeDef(Row::new(self, *file, *pos)))
})
})
.map(|(file, _)| self.files(*file))
.and_then(|file| file.assembly_name())
}
#[track_caller]
pub fn expect(&self, namespace: &str, name: &str) -> TypeDef<'_> {
let mut iter = self.get(namespace, name);
if let Some(def) = iter.next() {
if iter.next().is_none() {
def
} else {
panic!("more than one type found: {namespace}.{name}");
}
} else {
panic!("type not found: {namespace}.{name}")
}
}
pub fn nested(&self, ty: TypeDef) -> impl Iterator<Item = TypeDef<'_>> + '_ {
self.nested
.get(&(ty.0.file, ty.0.pos))
.into_iter()
.flatten()
.copied()
.map(move |pos| {
TypeDef(Row {
index: self,
file: ty.0.file,
pos,
})
})
.filter(|def| self.supports_architecture(*def))
}
pub fn nested_recursive<'a>(&'a self, ty: TypeDef<'a>) -> Vec<TypeDef<'a>> {
let mut out = Vec::new();
self.collect_nested(ty, &mut out);
out
}
fn collect_nested<'a>(&'a self, ty: TypeDef<'a>, out: &mut Vec<TypeDef<'a>>) {
for inner in self.nested(ty) {
out.push(inner);
self.collect_nested(inner, out);
}
}
fn supports_architecture<'a, R: HasAttributes<'a>>(&self, row: R) -> bool {
let arches = row.arches();
self.architecture == 0 || arches == 0 || arches & self.architecture != 0
}
pub fn namespaces(&self) -> impl Iterator<Item = &str> + '_ {
self.items.keys().map(String::as_str)
}
pub fn iter_items(&self) -> impl Iterator<Item = (&str, &str, Item<'_>)> + '_ {
self.items
.iter()
.flat_map(|(namespace, items)| {
items
.iter()
.map(move |(name, items)| (namespace.as_str(), name.as_str(), items))
})
.flat_map(move |(namespace, name, items)| {
items
.iter()
.map(move |raw| (namespace, name, self.item(*raw)))
})
}
pub fn items(&self) -> impl Iterator<Item = Item<'_>> + '_ {
self.items
.values()
.flat_map(|items| items.values())
.flatten()
.map(move |raw| self.item(*raw))
}
pub fn namespace_items(&self, namespace: &str) -> impl Iterator<Item = (&str, Item<'_>)> + '_ {
self.items
.get(namespace)
.into_iter()
.flatten()
.flat_map(move |(name, items)| {
items
.iter()
.map(move |raw| (name.as_str(), self.item(*raw)))
})
}
pub fn get_item(&self, namespace: &str, name: &str) -> impl Iterator<Item = Item<'_>> + '_ {
self.items
.get(namespace)
.and_then(|items| items.get(name))
.into_iter()
.flatten()
.map(move |raw| self.item(*raw))
}
#[track_caller]
pub fn expect_item(&self, namespace: &str, name: &str) -> Item<'_> {
let mut iter = self.get_item(namespace, name);
if let Some(item) = iter.next() {
if iter.next().is_none() {
item
} else {
panic!("more than one item found: {namespace}.{name}");
}
} else {
panic!("item not found: {namespace}.{name}")
}
}
fn item(&self, raw: RawItem) -> Item<'_> {
match raw {
RawItem::Type(file, pos) => Item::Type(TypeDef(Row::new(self, file, pos))),
RawItem::Fn(file, pos) => Item::Fn(MethodDef(Row::new(self, file, pos))),
RawItem::Const(file, pos) => Item::Const(Field(Row::new(self, file, pos))),
}
}
}