1use std::{collections::BTreeMap, str::FromStr};
2
3use crate::{
4 analysis::{functions::Info as FuncInfo, imports::Imports},
5 codegen::Visibility,
6 config::GObject,
7 library::{Type as LibType, TypeId},
8 version::Version,
9};
10
11#[derive(Clone, Copy, Eq, Debug, Ord, PartialEq, PartialOrd)]
12pub enum Type {
13 Compare,
14 Copy,
15 Equal,
16 Free,
17 Ref,
18 Display,
19 Unref,
20 Hash,
21}
22
23impl FromStr for Type {
24 type Err = String;
25
26 fn from_str(s: &str) -> Result<Self, Self::Err> {
27 match s {
28 "compare" => Ok(Self::Compare),
29 "copy" => Ok(Self::Copy),
30 "equal" => Ok(Self::Equal),
31 "free" | "destroy" => Ok(Self::Free),
32 "is_equal" => Ok(Self::Equal),
33 "ref" | "ref_" => Ok(Self::Ref),
34 "unref" => Ok(Self::Unref),
35 "hash" => Ok(Self::Hash),
36 _ => Err(format!("Unknown type '{s}'")),
37 }
38 }
39}
40
41#[derive(Debug, Clone)]
42pub struct TraitInfo {
43 pub glib_name: String,
44 pub version: Option<Version>,
45 pub first_parameter_mut: bool,
46}
47
48type TraitInfos = BTreeMap<Type, TraitInfo>;
49
50#[derive(Clone, Copy, Eq, Debug, Ord, PartialEq, PartialOrd)]
51pub enum FunctionType {
52 StaticStringify,
53}
54
55#[derive(Debug, Clone)]
56pub struct FunctionInfo {
57 pub type_: FunctionType,
58 pub version: Option<Version>,
59}
60
61type FunctionInfos = BTreeMap<String, FunctionInfo>;
62
63#[derive(Debug, Default)]
64pub struct Infos {
65 traits: TraitInfos,
66 functions: FunctionInfos,
67}
68
69impl Infos {
70 pub fn traits(&self) -> &TraitInfos {
71 &self.traits
72 }
73
74 pub fn traits_mut(&mut self) -> &mut TraitInfos {
75 &mut self.traits
76 }
77
78 pub fn has_trait(&self, type_: Type) -> bool {
79 self.traits.contains_key(&type_)
80 }
81
82 pub fn functions(&self) -> &FunctionInfos {
83 &self.functions
84 }
85}
86
87fn is_stringify(func: &mut FuncInfo, parent_type: &LibType, obj: &GObject) -> bool {
90 if func.parameters.c_parameters.len() != 1 {
91 return false;
92 }
93 if !func.parameters.c_parameters[0].instance_parameter {
94 return false;
95 }
96
97 if let Some(ret) = func.ret.parameter.as_mut() {
98 if ret.lib_par.typ != TypeId::tid_utf8() {
99 return false;
100 }
101
102 if func.name == "to_string" {
103 assert!(
105 func.new_name.is_none(),
106 "A `to_string` function can't be renamed manually. It's automatically renamed to `to_str`"
107 );
108 func.new_name = Some("to_str".to_owned());
109
110 if !obj.trust_return_value_nullability
114 && !matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_))
115 {
116 *ret.lib_par.nullable = false;
117 }
118 }
119
120 !*ret.lib_par.nullable
122 } else {
123 false
124 }
125}
126
127fn update_func(func: &mut FuncInfo, type_: Type) -> bool {
128 if !func.commented {
129 use self::Type::*;
130 match type_ {
131 Copy | Free | Ref | Unref => func.hidden = true,
132 Hash | Compare | Equal => func.visibility = Visibility::Private,
133 Display => func.visibility = Visibility::Public,
134 };
135 }
136 true
137}
138
139pub fn extract(functions: &mut [FuncInfo], parent_type: &LibType, obj: &GObject) -> Infos {
140 let mut specials = Infos::default();
141 let mut has_copy = false;
142 let mut has_free = false;
143 let mut destroy = None;
144
145 for (pos, func) in functions.iter_mut().enumerate() {
146 if is_stringify(func, parent_type, obj) {
147 let return_transfer_none = func
148 .ret
149 .parameter
150 .as_ref()
151 .is_some_and(|ret| ret.lib_par.transfer == crate::library::Transfer::None);
152
153 let returns_static_ref = return_transfer_none
155 && matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_))
156 && func.status.need_generate();
159
160 if returns_static_ref {
161 specials.functions.insert(
164 func.glib_name.clone(),
165 FunctionInfo {
166 type_: FunctionType::StaticStringify,
167 version: func.version,
168 },
169 );
170 }
171
172 if matches!(
174 func.name.as_str(),
175 "to_string" | "to_str" | "name" | "get_name"
176 ) {
177 specials.traits.insert(
180 Type::Display,
181 TraitInfo {
182 glib_name: func.glib_name.clone(),
183 version: func.version,
184 first_parameter_mut: false,
185 },
186 );
187 }
188 } else if let Ok(type_) = func.name.parse() {
189 if func.name == "destroy" {
190 destroy = Some((func.glib_name.clone(), pos));
191 continue;
192 }
193 if !update_func(func, type_) {
194 continue;
195 }
196 if func.name == "copy" {
197 has_copy = true;
198 } else if func.name == "free" {
199 has_free = true;
200 }
201
202 let first_parameter_mut = func
203 .parameters
204 .c_parameters
205 .first()
206 .is_some_and(|p| p.ref_mode == super::ref_mode::RefMode::ByRefMut);
207
208 specials.traits.insert(
209 type_,
210 TraitInfo {
211 glib_name: func.glib_name.clone(),
212 version: func.version,
213 first_parameter_mut,
214 },
215 );
216 }
217 }
218
219 if has_copy
220 && !has_free
221 && let Some((glib_name, pos)) = destroy
222 {
223 let ty_ = Type::from_str("destroy").unwrap();
224 let func = &mut functions[pos];
225 update_func(func, ty_);
226 specials.traits.insert(
227 ty_,
228 TraitInfo {
229 glib_name,
230 version: func.version,
231 first_parameter_mut: true,
232 },
233 );
234 }
235
236 specials
237}
238
239pub fn unhide(functions: &mut [FuncInfo], specials: &Infos, type_: Type) {
241 if let Some(func) = specials.traits().get(&type_) {
242 let func = functions
243 .iter_mut()
244 .find(|f| f.glib_name == func.glib_name && !f.commented);
245 if let Some(func) = func {
246 func.visibility = Visibility::Public;
247 func.hidden = false;
248 }
249 }
250}
251
252pub fn analyze_imports(specials: &Infos, imports: &mut Imports) {
253 for (type_, info) in specials.traits() {
254 use self::Type::*;
255 match type_ {
256 Copy if info.first_parameter_mut => {
257 imports.add_with_version("glib::translate::*", info.version);
258 }
259 Compare => {
260 imports.add_with_version("glib::translate::*", info.version);
261 }
262 Equal => imports.add_with_version("glib::translate::*", info.version),
263 _ => {}
264 }
265 }
266 for info in specials.functions().values() {
267 match info.type_ {
268 FunctionType::StaticStringify => {
269 imports.add_with_version("glib::GStr", info.version);
270 }
271 }
272 }
273}