1use std::{borrow::Cow, ops::Deref};
2
3use log::info;
4
5use super::{
6 child_properties::ChildProperties, imports::Imports, info_base::InfoBase,
7 signatures::Signatures, *,
8};
9use crate::{
10 config::gobjects::{GObject, GStatus},
11 env::Env,
12 library::{self, FunctionKind},
13 nameutil::*,
14 traits::*,
15};
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub enum LocationInObject {
20 Impl,
21 VirtualExt,
22 ClassExt,
23 ClassExtManual,
24 Ext,
25 ExtManual,
26 Builder,
27}
28
29#[derive(Debug, Default)]
30pub struct Info {
31 pub base: InfoBase,
32 pub c_type: String,
33 pub c_class_type: Option<String>,
34 pub get_type: String,
35 pub is_interface: bool,
36 pub is_fundamental: bool,
37 pub supertypes: Vec<general::StatusedTypeId>,
38 pub final_type: bool,
39 pub generate_trait: bool,
40 pub trait_name: String,
41 pub has_constructors: bool,
42 pub has_functions: bool,
43 pub virtual_methods: Vec<functions::Info>,
44 pub signals: Vec<signals::Info>,
45 pub notify_signals: Vec<signals::Info>,
46 pub properties: Vec<properties::Property>,
47 pub builder_properties: Vec<(Vec<properties::Property>, TypeId)>,
48 pub builder_postprocess: Option<String>,
49 pub child_properties: ChildProperties,
50 pub signatures: Signatures,
51 pub ref_fn: Option<String>,
53 pub unref_fn: Option<String>,
55}
56
57impl Info {
58 pub fn has_signals(&self) -> bool {
59 self.signals.iter().any(|s| s.trampoline.is_ok())
60 || self.notify_signals.iter().any(|s| s.trampoline.is_ok())
61 }
62
63 pub fn should_generate_impl_block(&self) -> bool {
70 self.has_constructors
71 || has_builder_properties(&self.builder_properties)
72 || !(self.need_generate_trait()
73 && self.methods().is_empty()
74 && self.properties.is_empty()
75 && self.child_properties.is_empty()
76 && self.signals.is_empty())
77 || self.has_functions
78 }
79
80 pub fn need_generate_inherent(&self) -> bool {
81 self.has_constructors
82 || self.has_functions
83 || !self.need_generate_trait()
84 || has_builder_properties(&self.builder_properties)
85 }
86
87 pub fn need_generate_trait(&self) -> bool {
88 self.generate_trait
89 }
90
91 pub fn has_action_signals(&self) -> bool {
92 self.signals.iter().any(|s| s.action_emit_name.is_some())
93 }
94
95 pub fn function_location(&self, fn_info: &functions::Info) -> LocationInObject {
97 if fn_info.kind == FunctionKind::ClassMethod {
98 LocationInObject::ClassExt
100 } else if fn_info.kind == FunctionKind::VirtualMethod {
101 LocationInObject::VirtualExt
103 } else if self.final_type
104 || self.is_fundamental
105 || matches!(
106 fn_info.kind,
107 FunctionKind::Constructor | FunctionKind::Function
108 )
109 {
110 LocationInObject::Impl
111 } else if fn_info.status == GStatus::Generate || self.full_name == "GObject.Object" {
112 LocationInObject::Ext
113 } else {
114 LocationInObject::ExtManual
115 }
116 }
117
118 pub fn generate_doc_link_info(
122 &self,
123 fn_info: &functions::Info,
124 ) -> (Cow<'_, str>, Cow<'_, str>) {
125 match self.function_location(fn_info) {
126 LocationInObject::Impl => (self.name.as_str().into(), self.name.as_str().into()),
127 LocationInObject::ExtManual => {
128 let trait_name = format!("{}Manual", self.trait_name);
129 (format!("prelude::{trait_name}").into(), trait_name.into())
130 }
131 LocationInObject::Ext => (
132 format!("prelude::{}", self.trait_name).into(),
133 self.trait_name.as_str().into(),
134 ),
135 LocationInObject::VirtualExt => {
136 let trait_name = format!("{}Impl", self.trait_name.trim_end_matches("Ext"));
138 (
139 format!("subclass::prelude::{trait_name}").into(),
140 trait_name.into(),
141 )
142 }
143 LocationInObject::ClassExt | LocationInObject::ClassExtManual => {
144 let trait_name = format!("{}Ext", self.trait_name);
145 (
146 format!("subclass::prelude::{}", trait_name).into(),
147 trait_name.into(),
148 )
149 }
150 LocationInObject::Builder => {
151 panic!("C documentation is not expected to link to builders (a Rust concept)!")
152 }
153 }
154 }
155}
156
157impl Deref for Info {
158 type Target = InfoBase;
159
160 fn deref(&self) -> &InfoBase {
161 &self.base
162 }
163}
164
165pub fn has_builder_properties(builder_properties: &[(Vec<properties::Property>, TypeId)]) -> bool {
166 builder_properties
167 .iter()
168 .map(|b| b.0.iter().len())
169 .sum::<usize>()
170 > 0
171}
172
173pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info> {
174 info!("Analyzing class {}", obj.name);
175 let full_name = obj.name.clone();
176
177 let class_tid = env.library.find_type(0, &full_name)?;
178
179 let type_ = env.type_(class_tid);
180
181 let name: String = split_namespace_name(&full_name).1.into();
182
183 let klass: &library::Class = type_.maybe_ref()?;
184
185 let version = obj.version.or(klass.version);
186 let deprecated_version = klass.deprecated_version;
187
188 let mut imports = Imports::with_defined(&env.library, &name);
189
190 let is_fundamental = obj.fundamental_type.unwrap_or(klass.is_fundamental);
191 let supertypes = supertypes::analyze(env, class_tid, version, &mut imports, is_fundamental);
192 let supertypes_properties = supertypes
193 .iter()
194 .filter_map(|t| match env.type_(t.type_id) {
195 Type::Class(c) => Some(&c.properties),
196 Type::Interface(i) => Some(&i.properties),
197 _ => None,
198 })
199 .flatten()
200 .collect::<Vec<&_>>();
201
202 let final_type = klass.final_type;
203 let trait_name = obj
204 .trait_name
205 .as_ref()
206 .cloned()
207 .unwrap_or_else(|| format!("{name}Ext"));
208
209 let mut signatures = Signatures::with_capacity(klass.functions.len());
210
211 let virtual_methods = functions::analyze(
214 env,
215 &klass.virtual_methods,
216 Some(class_tid),
217 true,
218 false,
219 obj,
220 &mut Imports::default(),
221 None,
222 Some(deps),
223 );
224
225 let mut functions = functions::analyze(
226 env,
227 &klass.functions,
228 Some(class_tid),
229 !final_type,
230 false,
231 obj,
232 &mut imports,
233 Some(&mut signatures),
234 Some(deps),
235 );
236 let mut specials = special_functions::extract(&mut functions, type_, obj);
237 special_functions::unhide(&mut functions, &specials, special_functions::Type::Copy);
239 for t in &[
242 special_functions::Type::Hash,
243 special_functions::Type::Equal,
244 special_functions::Type::Compare,
245 ] {
246 special_functions::unhide(&mut functions, &specials, *t);
247 specials.traits_mut().remove(t);
248 }
249 special_functions::analyze_imports(&specials, &mut imports);
250
251 let signals = signals::analyze(
252 env,
253 &klass.signals,
254 class_tid,
255 !final_type,
256 is_fundamental,
257 obj,
258 &mut imports,
259 );
260 let (properties, notify_signals) = properties::analyze(
261 env,
262 &klass.properties,
263 &supertypes_properties,
264 class_tid,
265 !final_type,
266 is_fundamental,
267 obj,
268 &mut imports,
269 &signatures,
270 deps,
271 &functions,
272 );
273
274 let builder_properties =
275 class_builder::analyze(env, &klass.properties, class_tid, obj, &mut imports);
276
277 let child_properties =
278 child_properties::analyze(env, obj.child_properties.as_ref(), class_tid, &mut imports);
279
280 let has_methods = functions
281 .iter()
282 .any(|f| f.kind == library::FunctionKind::Method && f.status.need_generate());
283 let has_signals = signals.iter().any(|s| s.trampoline.is_ok())
284 || notify_signals.iter().any(|s| s.trampoline.is_ok());
285 let generate_trait = !final_type
291 && !is_fundamental
292 && (has_signals || has_methods || !properties.is_empty() || !child_properties.is_empty());
293
294 imports.add("crate::ffi");
295 if is_fundamental {
296 imports.add("glib::translate::*");
297 imports.add("glib::prelude::*");
298 }
299
300 if has_builder_properties(&builder_properties) {
301 imports.add("glib::prelude::*");
302 }
303
304 if generate_trait {
305 imports.add("glib::prelude::*");
306 }
307
308 let base = InfoBase {
309 full_name,
310 type_id: class_tid,
311 name,
312 functions,
313 specials,
314 imports,
315 version,
316 deprecated_version,
317 cfg_condition: obj.cfg_condition.clone(),
318 concurrency: obj.concurrency,
319 visibility: obj.visibility,
320 };
321
322 if generate_trait {
324 let mut symbols = env.symbols.borrow_mut();
325 for func in base.methods() {
326 if let Some(symbol) = symbols.by_c_name_mut(&func.glib_name) {
327 symbol.make_trait_method(&trait_name);
328 }
329 }
330 }
331
332 let has_constructors = !base.constructors().is_empty();
333 let has_functions = !base.functions().is_empty();
334
335 let info = Info {
336 base,
337 c_type: klass.c_type.clone(),
338 c_class_type: klass.c_class_type.clone(),
339 get_type: klass.glib_get_type.clone(),
340 is_interface: false,
341 is_fundamental,
342 supertypes,
343 final_type,
344 generate_trait,
345 trait_name,
346 has_constructors,
347 has_functions,
348 virtual_methods,
349 signals,
350 notify_signals,
351 properties,
352 builder_properties,
353 builder_postprocess: obj.builder_postprocess.clone(),
354 child_properties,
355 signatures,
356 ref_fn: klass.ref_fn.clone(),
357 unref_fn: klass.unref_fn.clone(),
358 };
359
360 Some(info)
361}
362
363pub fn interface(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info> {
364 info!("Analyzing interface {}", obj.name);
365 let full_name = obj.name.clone();
366
367 let iface_tid = env.library.find_type(0, &full_name)?;
368
369 let type_ = env.type_(iface_tid);
370
371 let name: String = split_namespace_name(&full_name).1.into();
372
373 let iface: &library::Interface = type_.maybe_ref()?;
374
375 let version = obj.version.or(iface.version);
376 let deprecated_version = iface.deprecated_version;
377
378 let mut imports = Imports::with_defined(&env.library, &name);
379 imports.add("glib::prelude::*");
380 imports.add("crate::ffi");
381
382 let supertypes = supertypes::analyze(env, iface_tid, version, &mut imports, false);
383 let supertypes_properties = supertypes
384 .iter()
385 .filter_map(|t| match env.type_(t.type_id) {
386 Type::Class(c) => Some(&c.properties),
387 Type::Interface(i) => Some(&i.properties),
388 _ => None,
389 })
390 .flatten()
391 .collect::<Vec<&_>>();
392
393 let trait_name = obj
394 .trait_name
395 .as_ref()
396 .cloned()
397 .unwrap_or_else(|| format!("{name}Ext"));
398
399 let mut signatures = Signatures::with_capacity(iface.functions.len());
400
401 let functions = functions::analyze(
402 env,
403 &iface.functions,
404 Some(iface_tid),
405 true,
406 false,
407 obj,
408 &mut imports,
409 Some(&mut signatures),
410 Some(deps),
411 );
412
413 let signals = signals::analyze(
414 env,
415 &iface.signals,
416 iface_tid,
417 true,
418 false,
419 obj,
420 &mut imports,
421 );
422 let (properties, notify_signals) = properties::analyze(
423 env,
424 &iface.properties,
425 &supertypes_properties,
426 iface_tid,
427 true,
428 false,
429 obj,
430 &mut imports,
431 &signatures,
432 deps,
433 &functions,
434 );
435
436 let base = InfoBase {
437 full_name,
438 type_id: iface_tid,
439 name,
440 functions,
441 specials: Default::default(),
442 imports,
443 version,
444 deprecated_version,
445 cfg_condition: obj.cfg_condition.clone(),
446 concurrency: obj.concurrency,
447 visibility: obj.visibility,
448 };
449
450 let has_functions = !base.functions().is_empty();
451
452 let info = Info {
453 base,
454 c_type: iface.c_type.clone(),
455 c_class_type: iface.c_class_type.clone(),
456 get_type: iface.glib_get_type.clone(),
457 is_interface: true,
458 supertypes,
459 final_type: false,
460 generate_trait: true,
461 trait_name,
462 has_functions,
463 signals,
464 notify_signals,
465 properties,
466 signatures,
467 ..Default::default()
468 };
469
470 Some(info)
471}