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 child_type.is_none()
54        && let Some(config_child_type) = &config.child_type
55    {
56        let owner_name = RustType::try_new(env, type_tid).into_string();
57        error!("Bad child type `{config_child_type}` for `{owner_name}`");
58        return properties;
59    }
60
61    for prop in &config.properties {
62        if let Some(prop) =
63            analyze_property(env, prop, child_name, child_type, type_tid, config, imports)
64        {
65            properties.push(prop);
66        }
67    }
68
69    if !properties.is_empty() {
70        imports.add("glib::prelude::*");
71        if let Some(rust_type) = child_type.and_then(|typ| RustType::try_new(env, typ).ok()) {
72            imports.add_used_types(rust_type.used_types());
73        }
74    }
75
76    properties
77}
78
79fn analyze_property(
80    env: &Env,
81    prop: &config::ChildProperty,
82    child_name: &str,
83    child_type: Option<library::TypeId>,
84    type_tid: library::TypeId,
85    config: &config::ChildProperties,
86    imports: &mut Imports,
87) -> Option<ChildProperty> {
88    let name = prop.name.clone();
89    let prop_name = nameutil::signal_to_snake(&prop.name);
90    let getter_rename = config
91        .properties
92        .iter()
93        .find(|cp| cp.name == name)
94        .and_then(|cp| cp.rename_getter.clone());
95    let is_getter_renamed = getter_rename.is_some();
96    let mut getter_name = getter_rename.unwrap_or_else(|| prop_name.clone());
97
98    if let Some(typ) = env.library.find_type(0, &prop.type_name) {
99        let doc_hidden = prop.doc_hidden;
100
101        imports.add("glib::prelude::*");
102        if let Ok(rust_type) = RustType::try_new(env, typ) {
103            imports.add_used_types(rust_type.used_types());
104        }
105
106        let get_out_ref_mode = RefMode::of(env, typ, library::ParameterDirection::Return);
107        if !is_getter_renamed
108            && let Ok(new_name) = getter_rules::try_rename_getter_suffix(
109                &getter_name,
110                typ == library::TypeId::tid_bool(),
111            )
112        {
113            getter_name = new_name.unwrap();
114        }
115
116        let mut set_in_ref_mode = RefMode::of(env, typ, library::ParameterDirection::In);
117        if set_in_ref_mode == RefMode::ByRefMut {
118            set_in_ref_mode = RefMode::ByRef;
119        }
120        let nullable = library::Nullable(set_in_ref_mode.is_ref());
121
122        let mut bounds_str = String::new();
123        let dir = ParameterDirection::In;
124        let set_params = if let Some(bound) = Bounds::type_for(env, typ) {
125            let r_type = RustType::builder(env, typ)
126                .ref_mode(RefMode::ByRefFake)
127                .try_build()
128                .into_string();
129
130            let _bound = Bound {
131                bound_type: bound,
132                parameter_name: TYPE_PARAMETERS_START.to_string(),
133                alias: Some(TYPE_PARAMETERS_START.to_owned()),
134                type_str: r_type,
135                callback_modified: false,
136            };
137            // TODO: bounds_str push?!?!
138            bounds_str.push_str("TODO");
139            format!("{prop_name}: {TYPE_PARAMETERS_START}")
140            // let mut bounds = Bounds::default();
141            // bounds.add_parameter("P", &r_type, bound, false);
142            // let (s_bounds, _) = function::bounds(&bounds, &[], false);
143            // // Because the bounds won't necessarily be added into the final
144            // function, we // only keep the "inner" part to make
145            // the string computation easier. So // `<T: X>` becomes
146            // `T: X`. bounds_str.push_str(&s_bounds[1..s_bounds.
147            // len() - 1]); format!("{}: {}", prop_name,
148            // bounds.iter().last().unwrap().alias)
149        } else {
150            format!(
151                "{}: {}",
152                prop_name,
153                RustType::builder(env, typ)
154                    .direction(dir)
155                    .nullable(nullable)
156                    .ref_mode(set_in_ref_mode)
157                    .try_build_param()
158                    .into_string()
159            )
160        };
161
162        Some(ChildProperty {
163            name,
164            prop_name,
165            getter_name,
166            typ,
167            child_name: child_name.to_owned(),
168            child_type,
169            nullable,
170            get_out_ref_mode,
171            set_in_ref_mode,
172            doc_hidden,
173            set_params,
174            bounds: bounds_str,
175            to_glib_extra: String::new(),
176        })
177    } else {
178        let owner_name = RustType::try_new(env, type_tid).into_string();
179        error!(
180            "Bad type `{}` of child property `{}` for `{}`",
181            &prop.type_name, name, owner_name
182        );
183        None
184    }
185}