1use std::collections::BTreeMap;
2
3use imports::Imports;
4use log::error;
5
6use crate::{
7 env::Env,
8 library::{self, Type, TypeId},
9};
10
11pub mod bounds;
12pub mod c_type;
13pub mod child_properties;
14pub mod class_builder;
15pub mod class_hierarchy;
16pub mod constants;
17pub mod conversion_type;
18pub mod enums;
19pub mod ffi_type;
20pub mod flags;
21pub mod function_parameters;
22pub use function_parameters::Parameter;
23pub mod functions;
24pub mod general;
25pub mod imports;
26pub mod info_base;
27pub mod namespaces;
28pub mod object;
29pub mod out_parameters;
30mod override_string_type;
31pub mod properties;
32pub mod record;
33pub mod record_type;
34pub mod ref_mode;
35pub mod return_value;
36pub mod rust_type;
37pub mod safety_assertion_mode;
38pub mod signals;
39pub mod signatures;
40pub mod special_functions;
41pub mod supertypes;
42pub mod symbols;
43pub mod trampoline_parameters;
44pub mod trampolines;
45pub mod try_from_glib;
46pub mod types;
47
48#[derive(Debug, Default)]
49pub struct Analysis {
50 pub objects: BTreeMap<String, object::Info>,
51 pub records: BTreeMap<String, record::Info>,
52 pub global_functions: Option<info_base::InfoBase>,
53 pub constants: Vec<constants::Info>,
54
55 pub enumerations: Vec<enums::Info>,
56 pub enum_imports: Imports,
57
58 pub flags: Vec<flags::Info>,
59 pub flags_imports: Imports,
60}
61
62fn find_function<'a>(
63 env: &Env,
64 mut functions: impl Iterator<Item = &'a functions::Info>,
65 search_fn: impl Fn(&functions::Info) -> bool + Copy,
66) -> Option<&'a functions::Info> {
67 functions.find(|fn_info| fn_info.should_be_doc_linked(env) && search_fn(fn_info))
68}
69
70impl Analysis {
71 pub fn find_global_function<F: Fn(&functions::Info) -> bool + Copy>(
72 &self,
73 env: &Env,
74 search: F,
75 ) -> Option<&functions::Info> {
76 self.global_functions
77 .as_ref()
78 .and_then(move |info| find_function(env, info.functions.iter(), search))
79 }
80
81 pub fn find_record_by_function<
82 F: Fn(&functions::Info) -> bool + Copy,
83 G: Fn(&record::Info) -> bool + Copy,
84 >(
85 &self,
86 env: &Env,
87 search_record: G,
88 search_fn: F,
89 ) -> Option<(&record::Info, &functions::Info)> {
90 self.records
91 .values()
92 .filter(|r| search_record(r))
93 .find_map(|record_info| {
94 find_function(env, record_info.functions.iter(), search_fn)
95 .map(|fn_info| (record_info, fn_info))
96 })
97 }
98
99 pub fn find_object_by_virtual_method<
100 F: Fn(&functions::Info) -> bool + Copy,
101 G: Fn(&object::Info) -> bool + Copy,
102 >(
103 &self,
104 env: &Env,
105 search_obj: G,
106 search_fn: F,
107 ) -> Option<(&object::Info, &functions::Info)> {
108 self.objects
109 .values()
110 .filter(|o| search_obj(o))
111 .find_map(|obj_info| {
112 find_function(env, obj_info.virtual_methods.iter(), search_fn)
113 .map(|fn_info| (obj_info, fn_info))
114 })
115 }
116
117 pub fn find_object_by_function<
118 F: Fn(&functions::Info) -> bool + Copy,
119 G: Fn(&object::Info) -> bool + Copy,
120 >(
121 &self,
122 env: &Env,
123 search_obj: G,
124 search_fn: F,
125 ) -> Option<(&object::Info, &functions::Info)> {
126 self.objects
127 .values()
128 .filter(|o| search_obj(o))
129 .find_map(|obj_info| {
130 find_function(env, obj_info.functions.iter(), search_fn)
131 .map(|fn_info| (obj_info, fn_info))
132 })
133 }
134
135 pub fn find_enum_by_function<
136 F: Fn(&functions::Info) -> bool + Copy,
137 G: Fn(&enums::Info) -> bool + Copy,
138 >(
139 &self,
140 env: &Env,
141 search_enum: G,
142 search_fn: F,
143 ) -> Option<(&enums::Info, &functions::Info)> {
144 self.enumerations
145 .iter()
146 .filter(|o| search_enum(o))
147 .find_map(|obj_info| {
148 find_function(env, obj_info.functions.iter(), search_fn)
149 .map(|fn_info| (obj_info, fn_info))
150 })
151 }
152
153 pub fn find_flag_by_function<
154 F: Fn(&functions::Info) -> bool + Copy,
155 G: Fn(&flags::Info) -> bool + Copy,
156 >(
157 &self,
158 env: &Env,
159 search_flag: G,
160 search_fn: F,
161 ) -> Option<(&flags::Info, &functions::Info)> {
162 self.flags
163 .iter()
164 .filter(|o| search_flag(o))
165 .find_map(|obj_info| {
166 find_function(env, obj_info.functions.iter(), search_fn)
167 .map(|fn_info| (obj_info, fn_info))
168 })
169 }
170}
171
172pub fn run(env: &mut Env) {
173 let mut to_analyze: Vec<(TypeId, Vec<TypeId>)> = Vec::with_capacity(env.config.objects.len());
174 for obj in env.config.objects.values() {
175 if obj.status.ignored() {
176 continue;
177 }
178 let Some(tid) = env.library.find_type(0, &obj.name) else {
179 continue;
180 };
181 let deps = supertypes::dependencies(env, tid);
182 to_analyze.push((tid, deps));
183 }
184
185 let mut analyzed = 1;
186 while analyzed > 0 {
187 analyzed = 0;
188 let mut new_to_analyze: Vec<(TypeId, Vec<TypeId>)> = Vec::with_capacity(to_analyze.len());
189 for (tid, ref deps) in to_analyze {
190 if !is_all_deps_analyzed(env, deps) {
191 new_to_analyze.push((tid, deps.clone()));
192 continue;
193 }
194 analyze(env, tid, deps);
195 analyzed += 1;
196 }
197
198 to_analyze = new_to_analyze;
199 }
200
201 if !to_analyze.is_empty() {
202 error!(
203 "Not analyzed {} objects due unfinished dependencies",
204 to_analyze.len()
205 );
206 return;
207 }
208
209 analyze_enums(env);
210
211 analyze_flags(env);
212
213 analyze_constants(env);
214
215 analyze_global_functions(env);
217}
218
219fn analyze_enums(env: &mut Env) {
220 let mut imports = Imports::new(&env.library);
221
222 for obj in env.config.objects.values() {
223 if obj.status.ignored() {
224 continue;
225 }
226 let Some(tid) = env.library.find_type(0, &obj.name) else {
227 continue;
228 };
229
230 if let Type::Enumeration(_) = env.library.type_(tid) {
231 if let Some(info) = enums::new(env, obj, &mut imports) {
232 env.analysis.enumerations.push(info);
233 }
234 }
235 }
236
237 env.analysis.enum_imports = imports;
238}
239
240fn analyze_flags(env: &mut Env) {
241 let mut imports = Imports::new(&env.library);
242
243 for obj in env.config.objects.values() {
244 if obj.status.ignored() {
245 continue;
246 }
247 let Some(tid) = env.library.find_type(0, &obj.name) else {
248 continue;
249 };
250
251 if let Type::Bitfield(_) = env.library.type_(tid) {
252 if let Some(info) = flags::new(env, obj, &mut imports) {
253 env.analysis.flags.push(info);
254 }
255 }
256 }
257
258 env.analysis.flags_imports = imports;
259}
260
261fn analyze_global_functions(env: &mut Env) {
262 let ns = env.library.namespace(library::MAIN_NAMESPACE);
263
264 let full_name = format!("{}.*", ns.name);
265
266 let obj = match env.config.objects.get(&*full_name) {
267 Some(obj) if obj.status.need_generate() => obj,
268 _ => return,
269 };
270
271 let functions: Vec<_> = ns
272 .functions
273 .iter()
274 .filter(|f| f.kind == library::FunctionKind::Global)
275 .collect();
276 if functions.is_empty() {
277 return;
278 }
279
280 let mut imports = imports::Imports::new(&env.library);
281 imports.add("glib::translate::*");
282 imports.add("crate:ffi");
283
284 let functions = functions::analyze(
285 env,
286 &functions,
287 None,
288 false,
289 false,
290 obj,
291 &mut imports,
292 None,
293 None,
294 );
295
296 env.analysis.global_functions = Some(info_base::InfoBase {
297 full_name,
298 type_id: TypeId::tid_none(),
299 name: "*".into(),
300 functions,
301 imports,
302 ..Default::default()
303 });
304}
305
306fn analyze_constants(env: &mut Env) {
307 let ns = env.library.namespace(library::MAIN_NAMESPACE);
308
309 let full_name = format!("{}.*", ns.name);
310
311 let obj = match env.config.objects.get(&*full_name) {
312 Some(obj) if obj.status.need_generate() => obj,
313 _ => return,
314 };
315
316 let constants: Vec<_> = ns.constants.iter().collect();
317 if constants.is_empty() {
318 return;
319 }
320
321 env.analysis.constants = constants::analyze(env, &constants, obj);
322}
323
324fn analyze(env: &mut Env, tid: TypeId, deps: &[TypeId]) {
325 let full_name = tid.full_name(&env.library);
326 let Some(obj) = env.config.objects.get(&*full_name) else {
327 return;
328 };
329 match env.library.type_(tid) {
330 Type::Class(_) => {
331 if let Some(info) = object::class(env, obj, deps) {
332 env.analysis.objects.insert(full_name, info);
333 }
334 }
335 Type::Interface(_) => {
336 if let Some(info) = object::interface(env, obj, deps) {
337 env.analysis.objects.insert(full_name, info);
338 }
339 }
340 Type::Record(_) => {
341 if let Some(info) = record::new(env, obj) {
342 env.analysis.records.insert(full_name, info);
343 }
344 }
345 _ => {}
346 }
347}
348
349fn is_all_deps_analyzed(env: &Env, deps: &[TypeId]) -> bool {
350 for tid in deps {
351 let full_name = tid.full_name(&env.library);
352 if !env.analysis.objects.contains_key(&full_name) {
353 return false;
354 }
355 }
356 true
357}
358
359pub fn is_gpointer(s: &str) -> bool {
360 s == "gpointer" || s == "void*"
361}