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!(func.new_name.is_none(), "A `to_string` function can't be renamed manually. It's automatically renamed to `to_str`");
105 func.new_name = Some("to_str".to_owned());
106
107 if !obj.trust_return_value_nullability
111 && !matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_))
112 {
113 *ret.lib_par.nullable = false;
114 }
115 }
116
117 !*ret.lib_par.nullable
119 } else {
120 false
121 }
122}
123
124fn update_func(func: &mut FuncInfo, type_: Type) -> bool {
125 if !func.commented {
126 use self::Type::*;
127 match type_ {
128 Copy | Free | Ref | Unref => func.hidden = true,
129 Hash | Compare | Equal => func.visibility = Visibility::Private,
130 Display => func.visibility = Visibility::Public,
131 };
132 }
133 true
134}
135
136pub fn extract(functions: &mut [FuncInfo], parent_type: &LibType, obj: &GObject) -> Infos {
137 let mut specials = Infos::default();
138 let mut has_copy = false;
139 let mut has_free = false;
140 let mut destroy = None;
141
142 for (pos, func) in functions.iter_mut().enumerate() {
143 if is_stringify(func, parent_type, obj) {
144 let return_transfer_none = func
145 .ret
146 .parameter
147 .as_ref()
148 .is_some_and(|ret| ret.lib_par.transfer == crate::library::Transfer::None);
149
150 let returns_static_ref = return_transfer_none
152 && matches!(parent_type, LibType::Enumeration(_) | LibType::Bitfield(_))
153 && func.status.need_generate();
156
157 if returns_static_ref {
158 specials.functions.insert(
161 func.glib_name.clone(),
162 FunctionInfo {
163 type_: FunctionType::StaticStringify,
164 version: func.version,
165 },
166 );
167 }
168
169 if matches!(
171 func.name.as_str(),
172 "to_string" | "to_str" | "name" | "get_name"
173 ) {
174 specials.traits.insert(
177 Type::Display,
178 TraitInfo {
179 glib_name: func.glib_name.clone(),
180 version: func.version,
181 first_parameter_mut: false,
182 },
183 );
184 }
185 } else if let Ok(type_) = func.name.parse() {
186 if func.name == "destroy" {
187 destroy = Some((func.glib_name.clone(), pos));
188 continue;
189 }
190 if !update_func(func, type_) {
191 continue;
192 }
193 if func.name == "copy" {
194 has_copy = true;
195 } else if func.name == "free" {
196 has_free = true;
197 }
198
199 let first_parameter_mut = func
200 .parameters
201 .c_parameters
202 .first()
203 .is_some_and(|p| p.ref_mode == super::ref_mode::RefMode::ByRefMut);
204
205 specials.traits.insert(
206 type_,
207 TraitInfo {
208 glib_name: func.glib_name.clone(),
209 version: func.version,
210 first_parameter_mut,
211 },
212 );
213 }
214 }
215
216 if has_copy && !has_free {
217 if let Some((glib_name, pos)) = destroy {
218 let ty_ = Type::from_str("destroy").unwrap();
219 let func = &mut functions[pos];
220 update_func(func, ty_);
221 specials.traits.insert(
222 ty_,
223 TraitInfo {
224 glib_name,
225 version: func.version,
226 first_parameter_mut: true,
227 },
228 );
229 }
230 }
231
232 specials
233}
234
235pub fn unhide(functions: &mut [FuncInfo], specials: &Infos, type_: Type) {
237 if let Some(func) = specials.traits().get(&type_) {
238 let func = functions
239 .iter_mut()
240 .find(|f| f.glib_name == func.glib_name && !f.commented);
241 if let Some(func) = func {
242 func.visibility = Visibility::Public;
243 func.hidden = false;
244 }
245 }
246}
247
248pub fn analyze_imports(specials: &Infos, imports: &mut Imports) {
249 for (type_, info) in specials.traits() {
250 use self::Type::*;
251 match type_ {
252 Copy if info.first_parameter_mut => {
253 imports.add_with_version("glib::translate::*", info.version);
254 }
255 Compare => {
256 imports.add_with_version("glib::translate::*", info.version);
257 }
258 Equal => imports.add_with_version("glib::translate::*", info.version),
259 _ => {}
260 }
261 }
262 for info in specials.functions().values() {
263 match info.type_ {
264 FunctionType::StaticStringify => {
265 imports.add_with_version("glib::GStr", info.version);
266 }
267 }
268 }
269}