libgir/analysis/
object.rs

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/// The location of an item within the object
18#[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    /// Specific to fundamental types
52    pub ref_fn: Option<String>,
53    /// Specific to fundamental types
54    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    /// Whether we should generate an impl block for this object
64    /// We don't generate an impl block if the type doesn't have any of the
65    /// followings:
66    /// - Constructors / Functions / Builder properties (no build function)
67    /// - Is a final type & doesn't have either methods / properties / child
68    ///   properties / signals
69    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    /// Returns the location of the function within this object
96    pub fn function_location(&self, fn_info: &functions::Info) -> LocationInObject {
97        if fn_info.kind == FunctionKind::ClassMethod {
98            // TODO: Fix location here once we can auto generate virtual methods
99            LocationInObject::ClassExt
100        } else if fn_info.kind == FunctionKind::VirtualMethod {
101            // TODO: Fix location here once we can auto generate virtual methods
102            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    /// Generate doc name based on function location within this object
119    /// See also [`Self::function_location()`].
120    /// Returns `(item/crate path including type name, just the type name)`
121    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                // TODO: maybe a different config for subclass trait name?
137                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    // As we don't generate virtual methods yet, we don't pass imports here
212    // it would need to be fixed once work in generating virtual methods is done
213    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    // `copy` will duplicate an object while `clone` just adds a reference
238    special_functions::unhide(&mut functions, &specials, special_functions::Type::Copy);
239    // these are all automatically derived on objects and compare by pointer. If
240    // such functions exist they will provide additional functionality
241    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    // There's no point in generating a trait if there are no signals, methods,
286    // properties and child properties: it would be empty
287    //
288    // There's also no point in generating a trait for final types: there are no
289    // possible subtypes
290    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    // patch up trait methods in the symbol table
323    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}