1use std::{collections::HashMap, path::PathBuf};
2
3use serde::Serialize;
4
5use crate::{
6 Comment, GObjectType, SourceLocation, Statement, TypeInfo, VariableDecl,
7 model::{
8 doc::TypeDoc,
9 expression::Expression,
10 top_level::{FunctionDefItem, TopLevelItem},
11 types::EnumInfo,
12 },
13 top_level::{FunctionDeclItem, PreprocessorDirective, TypeDefItem, TypedefTarget},
14};
15
16#[derive(Debug, Clone, Serialize, Default)]
18pub struct Project {
19 #[serde(skip_serializing_if = "HashMap::is_empty")]
20 pub files: HashMap<PathBuf, FileModel>,
21}
22
23impl Project {
24 pub fn new() -> Self {
25 Self {
26 files: HashMap::new(),
27 }
28 }
29
30 pub fn get_file(&self, path: &PathBuf) -> Option<&FileModel> {
32 self.files.get(path)
33 }
34
35 pub fn is_function_declared_in_header(&self, name: &str) -> bool {
37 for file in self.files.values() {
38 if file.path.extension().is_some_and(|ext| ext == "h")
39 && file
40 .iter_function_declarations()
41 .any(|decl| decl.name == name)
42 {
43 return true;
44 }
45 }
46 false
47 }
48
49 pub fn is_function_exported(&self, name: &str) -> bool {
51 for file in self.files.values() {
52 if file
53 .iter_function_declarations()
54 .any(|decl| decl.name == name && !decl.export_macros.is_empty())
55 {
56 return true;
57 }
58 }
59 false
60 }
61}
62
63#[derive(Debug, Clone, Serialize)]
65pub struct FileModel {
66 pub path: PathBuf,
67 #[serde(skip_serializing_if = "Vec::is_empty")]
69 pub top_level_items: Vec<TopLevelItem>,
70 #[serde(skip)]
73 pub source: Vec<u8>,
74}
75
76impl FileModel {
77 pub fn new(path: PathBuf) -> Self {
78 Self {
79 path,
80 top_level_items: Vec::new(),
81 source: Vec::new(),
82 }
83 }
84
85 pub fn iter_all_includes(&self) -> impl Iterator<Item = (&str, bool, SourceLocation)> + '_ {
88 self.iter_items_recursive(&self.top_level_items)
89 .filter_map(|item| match item {
90 TopLevelItem::Preprocessor(PreprocessorDirective::Include {
91 path,
92 is_system,
93 location,
94 }) => Some((path.as_str(), *is_system, *location)),
95 _ => None,
96 })
97 }
98
99 pub fn iter_function_definitions(&self) -> impl Iterator<Item = &FunctionDefItem> + '_ {
102 self.iter_items_recursive(&self.top_level_items)
103 .filter_map(|item| match item {
104 TopLevelItem::FunctionDefinition(func) => Some(func),
105 _ => None,
106 })
107 }
108
109 pub fn iter_class_init_functions(&self) -> impl Iterator<Item = &FunctionDefItem> + '_ {
111 self.iter_function_definitions()
112 .filter(|f| f.name.ends_with("_class_init"))
113 }
114
115 pub fn iter_function_declarations(&self) -> impl Iterator<Item = &FunctionDeclItem> + '_ {
118 self.iter_items_recursive(&self.top_level_items)
119 .filter_map(|item| match item {
120 TopLevelItem::FunctionDeclaration(func) => Some(func),
121 _ => None,
122 })
123 }
124
125 pub fn iter_all_function_names(&self) -> impl Iterator<Item = &str> + '_ {
128 self.iter_items_recursive(&self.top_level_items)
129 .filter_map(|item| match item {
130 TopLevelItem::FunctionDefinition(func) => Some(func.name.as_str()),
131 TopLevelItem::FunctionDeclaration(func) => Some(func.name.as_str()),
132 _ => None,
133 })
134 }
135
136 pub fn iter_all_gobject_types(&self) -> impl Iterator<Item = &GObjectType> + '_ {
139 self.iter_items_recursive(&self.top_level_items)
140 .filter_map(|item| match item {
141 TopLevelItem::Preprocessor(PreprocessorDirective::GObjectType(gobject_type)) => {
142 Some(gobject_type.as_ref())
143 }
144 _ => None,
145 })
146 }
147
148 pub fn find_class_struct_for(&self, gobject_type: &GObjectType) -> Option<&TypeDefItem> {
152 let name = gobject_type.class_struct_name()?;
153 self.iter_class_structs().find(|td| {
154 if let TypeDefItem::Struct { name: n, .. } = td {
155 n.trim_start_matches('_') == name
156 } else {
157 false
158 }
159 })
160 }
161
162 pub fn iter_class_structs(&self) -> impl Iterator<Item = &TypeDefItem> + '_ {
165 self.iter_items_recursive(&self.top_level_items)
166 .filter_map(|item| match item {
167 TopLevelItem::TypeDefinition(td @ TypeDefItem::Struct { vfuncs, .. })
168 if !vfuncs.is_empty() =>
169 {
170 Some(td)
171 }
172 _ => None,
173 })
174 }
175
176 pub fn iter_comments(&self) -> impl Iterator<Item = &Comment> + '_ {
179 self.iter_items_recursive(&self.top_level_items)
180 .filter_map(|item| match item {
181 TopLevelItem::Comment(c) => Some(c),
182 _ => None,
183 })
184 }
185
186 pub fn iter_all_enums(&self) -> impl Iterator<Item = &EnumInfo> + '_ {
188 self.iter_items_recursive(&self.top_level_items)
189 .filter_map(|item| match item {
190 TopLevelItem::TypeDefinition(TypeDefItem::Enum(enum_info)) => {
191 Some(enum_info.as_ref())
192 }
193 _ => None,
194 })
195 }
196
197 pub fn iter_property_enums(&self) -> impl Iterator<Item = &EnumInfo> + '_ {
201 self.iter_all_enums().filter(|e| e.is_property_enum())
202 }
203
204 pub fn find_gobject_type_for_signal_enum(&self, enum_info: &EnumInfo) -> Option<&GObjectType> {
207 let signal_names: Vec<&str> = enum_info
208 .values
209 .iter()
210 .filter(|v| !v.is_signal_last())
211 .map(|v| v.name.as_str())
212 .collect();
213
214 let n_signals_name = enum_info
215 .values
216 .last()
217 .filter(|v| v.is_signal_last())
218 .map(|v| v.name.as_str());
219
220 let arrays = self.find_typed_arrays("guint", false, n_signals_name);
221 let array_names: Vec<&str> = arrays.iter().map(|d| d.name.as_str()).collect();
222
223 self.iter_all_gobject_types().find(|gt| {
224 let class_init_name = gt.class_init_function_name();
225 let Some(func) = self
226 .iter_function_definitions()
227 .find(|f| f.name == class_init_name)
228 else {
229 return false;
230 };
231
232 if !array_names.is_empty() {
234 let uses_array = func
235 .body_statements
236 .iter()
237 .flat_map(Statement::iter_assignments)
238 .any(|a| {
239 matches!(&*a.lhs, Expression::Subscript(sub)
240 if matches!(&*sub.array, Expression::Identifier(id)
241 if array_names.contains(&id.name.as_str())))
242 });
243 if uses_array {
244 return true;
245 }
246 }
247
248 func.body_statements
251 .iter()
252 .flat_map(Statement::iter_assignments)
253 .any(|a| {
254 if let Expression::Subscript(sub) = &*a.lhs
255 && let Expression::Identifier(index_id) = &*sub.index
256 && signal_names.contains(&index_id.name.as_str())
257 && let Expression::Call(call) = &*a.rhs
258 && call.function_contains("g_signal_new", &self.source)
259 {
260 true
261 } else {
262 false
263 }
264 })
265 })
266 }
267
268 pub fn find_typed_arrays(
281 &self,
282 base_type: &str,
283 is_pointer: bool,
284 sentinel_name: Option<&str>,
285 ) -> Vec<&VariableDecl> {
286 self.iter_all_items()
287 .filter_map(|item| {
288 let TopLevelItem::Declaration(decl) = item else {
289 return None;
290 };
291 if !decl.type_info.is_base_type(base_type)
292 || decl.type_info.is_pointer() != is_pointer
293 {
294 return None;
295 }
296 let matches = match &decl.array_size {
297 Some(Expression::Identifier(size_id)) => {
298 sentinel_name.is_none_or(|s| size_id.name == s)
299 }
300 Some(Expression::Binary(_)) => sentinel_name.is_none(),
301 Some(_) => sentinel_name.is_none(),
302 None => false,
303 };
304 matches.then_some(decl.as_ref())
305 })
306 .collect()
307 }
308
309 pub fn find_gobject_type_for_property_enum(
311 &self,
312 enum_info: &EnumInfo,
313 ) -> Option<&GObjectType> {
314 let property_names: Vec<&str> = enum_info
315 .values
316 .iter()
317 .filter(|v| !v.is_prop_0() && !v.is_prop_last())
318 .map(|v| v.name.as_str())
319 .collect();
320
321 let n_props_name = enum_info
322 .values
323 .last()
324 .filter(|v| v.is_prop_last())
325 .map(|v| v.name.as_str());
326
327 self.iter_all_gobject_types().find(|gt| {
328 let has_matching_property = gt.properties.iter().any(|a| {
330 a.get_installed_enum_value(&self.source)
331 .is_some_and(|ev| property_names.contains(&ev))
332 });
333 if has_matching_property {
334 return true;
335 }
336
337 if let Some(sentinel) = n_props_name {
340 let class_init_name = gt.class_init_function_name();
341 if let Some(func) = self
342 .iter_function_definitions()
343 .find(|f| f.name == class_init_name)
344 {
345 let install_calls = func.find_install_properties_calls();
346 return install_calls.iter().any(|call| {
347 call.get_arg(1)
348 .and_then(|arg| arg.to_source_string(&self.source))
349 .is_some_and(|name| name == sentinel)
350 });
351 }
352 }
353
354 false
355 })
356 }
357
358 pub fn iter_all_items(&self) -> impl Iterator<Item = &TopLevelItem> + '_ {
362 self.iter_items_recursive(&self.top_level_items)
363 }
364
365 pub fn iter_typedef_pairs(&self) -> impl Iterator<Item = (&str, &TypeInfo)> + '_ {
370 self.iter_all_items().filter_map(|item| match item {
371 TopLevelItem::TypeDefinition(TypeDefItem::Typedef {
372 name,
373 target: TypedefTarget::Type(target_type),
374 struct_fields,
375 ..
376 }) if struct_fields.is_empty() && !target_type.base_type.is_empty() => {
377 Some((name.as_str(), target_type))
378 }
379 _ => None,
380 })
381 }
382
383 pub fn resolve_gobject_types(&mut self) {
387 Self::resolve_items(&mut self.top_level_items, &self.source);
388 }
389
390 fn resolve_items(items: &mut [TopLevelItem], source: &[u8]) {
391 for i in 0..items.len() {
392 match &items[i] {
393 TopLevelItem::Preprocessor(PreprocessorDirective::GObjectType(gt)) => {
394 let class_init_name = gt.class_init_function_name();
395 let type_name = gt.type_name.clone();
396 let func_idx = items.iter().position(|item| {
397 matches!(item, TopLevelItem::FunctionDefinition(f) if f.name == class_init_name)
398 });
399 if let Some(j) = func_idx {
400 let func = match &items[j] {
401 TopLevelItem::FunctionDefinition(f) => f,
402 _ => unreachable!(),
403 };
404
405 let properties = func.find_param_spec_assignments(source);
406 let signals = func.find_signal_registrations(source);
407 if let TopLevelItem::Preprocessor(PreprocessorDirective::GObjectType(gt)) =
408 &mut items[i]
409 {
410 gt.properties = properties;
411 gt.signals = signals;
412 }
413 }
414
415 let doc = items.iter().find_map(|item| {
416 if let TopLevelItem::Comment(c) = item {
417 let td = TypeDoc::from_comment(c)?;
418 if td.symbol.as_deref() == Some(type_name.as_str()) {
419 return Some(td);
420 }
421 }
422 None
423 });
424 if let Some(doc) = doc
425 && let TopLevelItem::Preprocessor(PreprocessorDirective::GObjectType(gt)) =
426 &mut items[i]
427 {
428 gt.doc = Some(doc);
429 }
430 }
431 TopLevelItem::Preprocessor(PreprocessorDirective::Conditional { .. })
432 | TopLevelItem::Preprocessor(PreprocessorDirective::GObjectDeclsBlock { .. }) => {}
433 _ => {}
434 }
435 }
436
437 for item in items.iter_mut() {
438 match item {
439 TopLevelItem::Preprocessor(PreprocessorDirective::Conditional { body, .. })
440 | TopLevelItem::Preprocessor(PreprocessorDirective::GObjectDeclsBlock {
441 body,
442 ..
443 }) => {
444 Self::resolve_items(body, source);
445 }
446 _ => {}
447 }
448 }
449 }
450
451 fn iter_items_recursive<'a>(
453 &'a self,
454 items: &'a [TopLevelItem],
455 ) -> Box<dyn Iterator<Item = &'a TopLevelItem> + 'a> {
456 Box::new(items.iter().flat_map(move |item| match item {
457 TopLevelItem::Preprocessor(PreprocessorDirective::Conditional { body, .. })
458 | TopLevelItem::Preprocessor(PreprocessorDirective::GObjectDeclsBlock {
459 body, ..
460 }) => Box::new(std::iter::once(item).chain(self.iter_items_recursive(body)))
461 as Box<dyn Iterator<Item = &'a TopLevelItem>>,
462 _ => Box::new(std::iter::once(item)) as Box<dyn Iterator<Item = &'a TopLevelItem>>,
463 }))
464 }
465}