libgir/analysis/
child_properties.rs1use 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 bounds_str.push_str("TODO");
139 format!("{prop_name}: {TYPE_PARAMETERS_START}")
140 } 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}