1use super::*;
2
3pub struct Index {
5 files: Vec<File>,
6 types: HashMap<String, HashMap<String, Vec<(usize, usize)>>>,
7 items: HashMap<String, HashMap<String, Vec<RawItem>>>,
8 nested: HashMap<(usize, usize), Vec<usize>>,
9 architecture: i32,
10}
11
12#[derive(Copy, Clone)]
13enum RawItem {
14 Type(usize, usize), Fn(usize, usize), Const(usize, usize), }
18
19#[derive(Copy, Clone, Debug)]
21pub enum Item<'a> {
22 Type(TypeDef<'a>),
23 Fn(MethodDef<'a>),
24 Const(Field<'a>),
25}
26
27impl Index {
28 #[must_use]
30 pub fn read<P: AsRef<std::path::Path>>(path: P) -> Option<Self> {
31 Some(Self::new(vec![File::read(path)?]))
32 }
33
34 #[must_use]
36 pub fn new(files: Vec<File>) -> Self {
37 Self::new_for_architecture(files, 0)
38 }
39
40 #[must_use]
43 pub fn new_for_architecture(files: Vec<File>, architecture: i32) -> Self {
44 let mut types: HashMap<String, HashMap<String, Vec<(usize, usize)>>> = HashMap::new();
46 let mut nested: HashMap<(usize, usize), Vec<usize>> = HashMap::new();
47
48 for (file_pos, file) in files.iter().enumerate() {
49 for def_pos in file.TypeDef() {
50 let namespace = file.str(def_pos, TypeDef::TABLE, 2);
51
52 if namespace.is_empty() {
53 continue;
54 }
55
56 let name = file.str(def_pos, TypeDef::TABLE, 1);
57
58 types
59 .entry(namespace.to_string())
60 .or_default()
61 .entry(trim_tick(name).to_string())
62 .or_default()
63 .push((file_pos, def_pos));
64 }
65
66 for map in file.NestedClass() {
67 let inner = file.usize(map, NestedClass::TABLE, 0) - 1;
68 let outer = file.usize(map, NestedClass::TABLE, 1) - 1;
69 nested.entry((file_pos, outer)).or_default().push(inner);
70 }
71 }
72
73 let mut index = Self {
74 files,
75 types,
76 items: HashMap::new(),
77 nested,
78 architecture,
79 };
80
81 let mut items: HashMap<String, HashMap<String, Vec<RawItem>>> = HashMap::new();
83
84 for (namespace, name, ty) in index.iter() {
85 let apis = !ty.flags().contains(TypeAttributes::WindowsRuntime)
86 && ty.category() == TypeCategory::Class
87 && name == "Apis";
88
89 if apis {
90 for method in ty
91 .methods()
92 .filter(|method| index.supports_architecture(*method))
93 {
94 items
95 .entry(namespace.to_string())
96 .or_default()
97 .entry(method.name().to_string())
98 .or_default()
99 .push(RawItem::Fn(ty.0.file, method.0.pos));
100 }
101 for field in ty
102 .fields()
103 .filter(|field| index.supports_architecture(*field))
104 {
105 items
106 .entry(namespace.to_string())
107 .or_default()
108 .entry(field.name().to_string())
109 .or_default()
110 .push(RawItem::Const(ty.0.file, field.0.pos));
111 }
112 } else {
113 items
114 .entry(namespace.to_string())
115 .or_default()
116 .entry(name.to_string())
117 .or_default()
118 .push(RawItem::Type(ty.0.file, ty.0.pos));
119 }
120 }
121
122 index.items = items;
123 index
124 }
125
126 #[must_use]
128 pub fn leak(self) -> &'static Self {
129 Box::leak(Box::new(self))
130 }
131
132 #[must_use]
134 pub fn read_static<P: AsRef<std::path::Path>>(path: P) -> Option<&'static Self> {
135 Some(Self::new(vec![File::read(path)?]).leak())
136 }
137
138 pub(crate) fn files(&self, pos: usize) -> &File {
139 &self.files[pos]
140 }
141
142 pub fn iter(&self) -> impl Iterator<Item = (&str, &str, TypeDef<'_>)> + '_ {
144 self.types
145 .iter()
146 .flat_map(|(namespace, types)| {
147 types
148 .iter()
149 .map(move |(name, types)| (namespace.as_str(), name.as_str(), types))
150 })
151 .flat_map(|(namespace, name, types)| types.iter().map(move |ty| (namespace, name, ty)))
152 .map(|(namespace, name, (file, pos))| {
153 (namespace, name, TypeDef(Row::new(self, *file, *pos)))
154 })
155 .filter(|(_, _, def)| self.supports_architecture(*def))
156 }
157
158 pub fn types(&self) -> impl Iterator<Item = TypeDef<'_>> + '_ {
160 self.types
161 .values()
162 .flat_map(|types| types.values())
163 .flatten()
164 .map(|(file, pos)| TypeDef(Row::new(self, *file, *pos)))
165 .filter(|def| self.supports_architecture(*def))
166 }
167
168 pub fn get(&self, namespace: &str, name: &str) -> impl Iterator<Item = TypeDef<'_>> + '_ {
170 self.types
171 .get(namespace)
172 .and_then(|types| types.get(name))
173 .into_iter()
174 .flatten()
175 .map(|(file, pos)| TypeDef(Row::new(self, *file, *pos)))
176 .filter(|def| self.supports_architecture(*def))
177 }
178
179 pub fn contains_namespace(&self, namespace: &str) -> bool {
181 self.types.get(namespace).is_some_and(|types| {
182 types
183 .values()
184 .flatten()
185 .any(|(file, pos)| self.supports_architecture(TypeDef(Row::new(self, *file, *pos))))
186 })
187 }
188
189 pub fn contains(&self, namespace: &str, name: &str) -> bool {
191 self.get(namespace, name).next().is_some()
192 }
193
194 pub fn assembly_name(&self, namespace: &str, name: &str) -> Option<&str> {
196 self.types
197 .get(namespace)
198 .and_then(|types| types.get(name))
199 .and_then(|types| {
200 types.iter().find(|(file, pos)| {
201 self.supports_architecture(TypeDef(Row::new(self, *file, *pos)))
202 })
203 })
204 .map(|(file, _)| self.files(*file))
205 .and_then(|file| file.assembly_name())
206 }
207
208 #[track_caller]
211 pub fn expect(&self, namespace: &str, name: &str) -> TypeDef<'_> {
212 let mut iter = self.get(namespace, name);
213
214 if let Some(def) = iter.next() {
215 if iter.next().is_none() {
216 def
217 } else {
218 panic!("more than one type found: {namespace}.{name}");
219 }
220 } else {
221 panic!("type not found: {namespace}.{name}")
222 }
223 }
224
225 pub fn nested(&self, ty: TypeDef) -> impl Iterator<Item = TypeDef<'_>> + '_ {
227 self.nested
228 .get(&(ty.0.file, ty.0.pos))
229 .into_iter()
230 .flatten()
231 .copied()
232 .map(move |pos| {
233 TypeDef(Row {
234 index: self,
235 file: ty.0.file,
236 pos,
237 })
238 })
239 .filter(|def| self.supports_architecture(*def))
240 }
241
242 pub fn nested_recursive<'a>(&'a self, ty: TypeDef<'a>) -> Vec<TypeDef<'a>> {
244 let mut out = Vec::new();
245 self.collect_nested(ty, &mut out);
246 out
247 }
248
249 fn collect_nested<'a>(&'a self, ty: TypeDef<'a>, out: &mut Vec<TypeDef<'a>>) {
250 for inner in self.nested(ty) {
251 out.push(inner);
252 self.collect_nested(inner, out);
253 }
254 }
255
256 fn supports_architecture<'a, R: HasAttributes<'a>>(&self, row: R) -> bool {
257 let arches = row.arches();
258 self.architecture == 0 || arches == 0 || arches & self.architecture != 0
259 }
260
261 pub fn namespaces(&self) -> impl Iterator<Item = &str> + '_ {
263 self.items.keys().map(String::as_str)
264 }
265
266 pub fn iter_items(&self) -> impl Iterator<Item = (&str, &str, Item<'_>)> + '_ {
268 self.items
269 .iter()
270 .flat_map(|(namespace, items)| {
271 items
272 .iter()
273 .map(move |(name, items)| (namespace.as_str(), name.as_str(), items))
274 })
275 .flat_map(move |(namespace, name, items)| {
276 items
277 .iter()
278 .map(move |raw| (namespace, name, self.item(*raw)))
279 })
280 }
281
282 pub fn items(&self) -> impl Iterator<Item = Item<'_>> + '_ {
284 self.items
285 .values()
286 .flat_map(|items| items.values())
287 .flatten()
288 .map(move |raw| self.item(*raw))
289 }
290
291 pub fn namespace_items(&self, namespace: &str) -> impl Iterator<Item = (&str, Item<'_>)> + '_ {
293 self.items
294 .get(namespace)
295 .into_iter()
296 .flatten()
297 .flat_map(move |(name, items)| {
298 items
299 .iter()
300 .map(move |raw| (name.as_str(), self.item(*raw)))
301 })
302 }
303
304 pub fn get_item(&self, namespace: &str, name: &str) -> impl Iterator<Item = Item<'_>> + '_ {
306 self.items
307 .get(namespace)
308 .and_then(|items| items.get(name))
309 .into_iter()
310 .flatten()
311 .map(move |raw| self.item(*raw))
312 }
313
314 #[track_caller]
317 pub fn expect_item(&self, namespace: &str, name: &str) -> Item<'_> {
318 let mut iter = self.get_item(namespace, name);
319
320 if let Some(item) = iter.next() {
321 if iter.next().is_none() {
322 item
323 } else {
324 panic!("more than one item found: {namespace}.{name}");
325 }
326 } else {
327 panic!("item not found: {namespace}.{name}")
328 }
329 }
330
331 fn item(&self, raw: RawItem) -> Item<'_> {
332 match raw {
333 RawItem::Type(file, pos) => Item::Type(TypeDef(Row::new(self, file, pos))),
334 RawItem::Fn(file, pos) => Item::Fn(MethodDef(Row::new(self, file, pos))),
335 RawItem::Const(file, pos) => Item::Const(Field(Row::new(self, file, pos))),
336 }
337 }
338}