libgir/analysis/
child_properties.rs

1use log::error;
2
3use crate::{
4    analysis::{
5        bounds::{Bound, Bounds},
6        imports::Imports,
7        ref_mode::RefMode,
8        rust_type::RustType,
9    },
10    config,
11    consts::TYPE_PARAMETERS_START,
12    env::Env,
13    library::{self, ParameterDirection},
14    nameutil,
15    traits::*,
16};
17
18#[derive(Clone, Debug)]
19pub struct ChildProperty {
20    pub name: String,
21    pub prop_name: String,
22    pub getter_name: String,
23    pub typ: library::TypeId,
24    pub child_name: String,
25    pub child_type: Option<library::TypeId>,
26    pub nullable: library::Nullable,
27    pub get_out_ref_mode: RefMode,
28    pub set_in_ref_mode: RefMode,
29    pub doc_hidden: bool,
30    pub set_params: String,
31    pub bounds: String,
32    pub to_glib_extra: String,
33}
34
35pub type ChildProperties = Vec<ChildProperty>;
36
37pub fn analyze(
38    env: &Env,
39    config: Option<&config::ChildProperties>,
40    type_tid: library::TypeId,
41    imports: &mut Imports,
42) -> ChildProperties {
43    let mut properties = Vec::new();
44    if config.is_none() {
45        return properties;
46    }
47    let config = config.unwrap();
48    let child_name = config.child_name.as_ref().map_or("child", |s| s.as_str());
49    let child_type = config
50        .child_type
51        .as_ref()
52        .and_then(|name| env.library.find_type(0, name));
53    if config.child_type.is_some() && child_type.is_none() {
54        let owner_name = RustType::try_new(env, type_tid).into_string();
55        let child_type: &str = config.child_type.as_ref().unwrap();
56        error!("Bad child type `{}` for `{}`", child_type, owner_name);
57        return properties;
58    }
59
60    for prop in &config.properties {
61        if let Some(prop) =
62            analyze_property(env, prop, child_name, child_type, type_tid, config, imports)
63        {
64            properties.push(prop);
65        }
66    }
67
68    if !properties.is_empty() {
69        imports.add("glib::prelude::*");
70        if let Some(rust_type) = child_type.and_then(|typ| RustType::try_new(env, typ).ok()) {
71            imports.add_used_types(rust_type.used_types());
72        }
73    }
74
75    properties
76}
77
78fn analyze_property(
79    env: &Env,
80    prop: &config::ChildProperty,
81    child_name: &str,
82    child_type: Option<library::TypeId>,
83    type_tid: library::TypeId,
84    config: &config::ChildProperties,
85    imports: &mut Imports,
86) -> Option<ChildProperty> {
87    let name = prop.name.clone();
88    let prop_name = nameutil::signal_to_snake(&prop.name);
89    let getter_rename = config
90        .properties
91        .iter()
92        .find(|cp| cp.name == name)
93        .and_then(|cp| cp.rename_getter.clone());
94    let is_getter_renamed = getter_rename.is_some();
95    let mut getter_name = getter_rename.unwrap_or_else(|| prop_name.clone());
96
97    if let Some(typ) = env.library.find_type(0, &prop.type_name) {
98        let doc_hidden = prop.doc_hidden;
99
100        imports.add("glib::prelude::*");
101        if let Ok(rust_type) = RustType::try_new(env, typ) {
102            imports.add_used_types(rust_type.used_types());
103        }
104
105        let get_out_ref_mode = RefMode::of(env, typ, library::ParameterDirection::Return);
106        if !is_getter_renamed {
107            if let Ok(new_name) = getter_rules::try_rename_getter_suffix(
108                &getter_name,
109                typ == library::TypeId::tid_bool(),
110            ) {
111                getter_name = new_name.unwrap();
112            }
113        }
114
115        let mut set_in_ref_mode = RefMode::of(env, typ, library::ParameterDirection::In);
116        if set_in_ref_mode == RefMode::ByRefMut {
117            set_in_ref_mode = RefMode::ByRef;
118        }
119        let nullable = library::Nullable(set_in_ref_mode.is_ref());
120
121        let mut bounds_str = String::new();
122        let dir = ParameterDirection::In;
123        let set_params = if let Some(bound) = Bounds::type_for(env, typ) {
124            let r_type = RustType::builder(env, typ)
125                .ref_mode(RefMode::ByRefFake)
126                .try_build()
127                .into_string();
128
129            let _bound = Bound {
130                bound_type: bound,
131                parameter_name: TYPE_PARAMETERS_START.to_string(),
132                alias: Some(TYPE_PARAMETERS_START.to_owned()),
133                type_str: r_type,
134                callback_modified: false,
135            };
136            // TODO: bounds_str push?!?!
137            bounds_str.push_str("TODO");
138            format!("{prop_name}: {TYPE_PARAMETERS_START}")
139            // let mut bounds = Bounds::default();
140            // bounds.add_parameter("P", &r_type, bound, false);
141            // let (s_bounds, _) = function::bounds(&bounds, &[], false);
142            // // Because the bounds won't necessarily be added into the final
143            // function, we // only keep the "inner" part to make
144            // the string computation easier. So // `<T: X>` becomes
145            // `T: X`. bounds_str.push_str(&s_bounds[1..s_bounds.
146            // len() - 1]); format!("{}: {}", prop_name,
147            // bounds.iter().last().unwrap().alias)
148        } else {
149            format!(
150                "{}: {}",
151                prop_name,
152                RustType::builder(env, typ)
153                    .direction(dir)
154                    .nullable(nullable)
155                    .ref_mode(set_in_ref_mode)
156                    .try_build_param()
157                    .into_string()
158            )
159        };
160
161        Some(ChildProperty {
162            name,
163            prop_name,
164            getter_name,
165            typ,
166            child_name: child_name.to_owned(),
167            child_type,
168            nullable,
169            get_out_ref_mode,
170            set_in_ref_mode,
171            doc_hidden,
172            set_params,
173            bounds: bounds_str,
174            to_glib_extra: String::new(),
175        })
176    } else {
177        let owner_name = RustType::try_new(env, type_tid).into_string();
178        error!(
179            "Bad type `{}` of child property `{}` for `{}`",
180            &prop.type_name, name, owner_name
181        );
182        None
183    }
184}