1use std::{
2 io::{Result, Write},
3 sync::OnceLock,
4};
5
6use super::ffi_type::*;
7use crate::{
8 codegen::general::{cfg_condition, version_condition},
9 config::{functions::Function, gobjects::GObject},
10 env::Env,
11 library, nameutil,
12 traits::*,
13};
14
15const INTERN: &str = "intern";
17
18fn default_obj() -> &'static GObject {
19 static OBJ: OnceLock<GObject> = OnceLock::new();
20 OBJ.get_or_init(Default::default)
21}
22
23pub fn generate_records_funcs(
24 w: &mut dyn Write,
25 env: &Env,
26 records: &[&library::Record],
27) -> Result<()> {
28 let intern_str = INTERN.to_string();
29 for record in records {
30 let name = format!("{}.{}", env.config.library_name, record.name);
31 let obj = env.config.objects.get(&name).unwrap_or(default_obj());
32 let version = obj.version.or(record.version);
33 let glib_get_type = record.glib_get_type.as_ref().unwrap_or(&intern_str);
34 generate_object_funcs(
35 w,
36 env,
37 obj,
38 version,
39 &record.c_type,
40 glib_get_type,
41 &record.functions,
42 )?;
43 }
44
45 Ok(())
46}
47
48pub fn generate_classes_funcs(
49 w: &mut dyn Write,
50 env: &Env,
51 classes: &[&library::Class],
52) -> Result<()> {
53 for klass in classes {
54 let name = format!("{}.{}", env.config.library_name, klass.name);
55 let obj = env.config.objects.get(&name).unwrap_or(default_obj());
56 let version = obj.version.or(klass.version);
57 generate_object_funcs(
58 w,
59 env,
60 obj,
61 version,
62 &klass.c_type,
63 &klass.glib_get_type,
64 &klass.functions,
65 )?;
66 }
67
68 Ok(())
69}
70
71pub fn generate_bitfields_funcs(
72 w: &mut dyn Write,
73 env: &Env,
74 bitfields: &[&library::Bitfield],
75) -> Result<()> {
76 let intern_str = INTERN.to_string();
77 for bitfield in bitfields {
78 let name = format!("{}.{}", env.config.library_name, bitfield.name);
79 let obj = env.config.objects.get(&name).unwrap_or(default_obj());
80 let version = obj.version.or(bitfield.version);
81 let glib_get_type = bitfield.glib_get_type.as_ref().unwrap_or(&intern_str);
82 generate_object_funcs(
83 w,
84 env,
85 obj,
86 version,
87 &bitfield.c_type,
88 glib_get_type,
89 &bitfield.functions,
90 )?;
91 }
92
93 Ok(())
94}
95
96pub fn generate_enums_funcs(
97 w: &mut dyn Write,
98 env: &Env,
99 enums: &[&library::Enumeration],
100) -> Result<()> {
101 let intern_str = INTERN.to_string();
102 for en in enums {
103 let name = format!("{}.{}", env.config.library_name, en.name);
104 let obj = env.config.objects.get(&name).unwrap_or(default_obj());
105 let version = obj.version.or(en.version);
106 let glib_get_type = en.glib_get_type.as_ref().unwrap_or(&intern_str);
107 generate_object_funcs(
108 w,
109 env,
110 obj,
111 version,
112 &en.c_type,
113 glib_get_type,
114 &en.functions,
115 )?;
116 }
117
118 Ok(())
119}
120
121pub fn generate_unions_funcs(
122 w: &mut dyn Write,
123 env: &Env,
124 unions: &[&library::Union],
125) -> Result<()> {
126 let intern_str = INTERN.to_string();
127 for union in unions {
128 let Some(ref c_type) = union.c_type else {
129 return Ok(());
130 };
131 let name = format!("{}.{}", env.config.library_name, union.name);
132 let obj = env.config.objects.get(&name).unwrap_or(default_obj());
133 let glib_get_type = union.glib_get_type.as_ref().unwrap_or(&intern_str);
134 generate_object_funcs(
135 w,
136 env,
137 obj,
138 obj.version,
139 c_type,
140 glib_get_type,
141 &union.functions,
142 )?;
143 }
144
145 Ok(())
146}
147
148pub fn generate_interfaces_funcs(
149 w: &mut dyn Write,
150 env: &Env,
151 interfaces: &[&library::Interface],
152) -> Result<()> {
153 for interface in interfaces {
154 let name = format!("{}.{}", env.config.library_name, interface.name);
155 let obj = env.config.objects.get(&name).unwrap_or(default_obj());
156 let version = obj.version.or(interface.version);
157 generate_object_funcs(
158 w,
159 env,
160 obj,
161 version,
162 &interface.c_type,
163 &interface.glib_get_type,
164 &interface.functions,
165 )?;
166 }
167
168 Ok(())
169}
170
171pub fn generate_other_funcs(
172 w: &mut dyn Write,
173 env: &Env,
174 functions: &[library::Function],
175) -> Result<()> {
176 let name = format!("{}.*", env.config.library_name);
177 let obj = env.config.objects.get(&name).unwrap_or(default_obj());
178 generate_object_funcs(w, env, obj, None, "Other functions", INTERN, functions)
179}
180
181fn generate_cfg_configure(
182 w: &mut dyn Write,
183 configured_functions: &[&Function],
184 commented: bool,
185) -> Result<()> {
186 let cfg_condition_ = configured_functions
187 .iter()
188 .find_map(|f| f.cfg_condition.as_ref());
189 cfg_condition(w, cfg_condition_, commented, 1)?;
190 Ok(())
191}
192
193fn generate_object_funcs(
194 w: &mut dyn Write,
195 env: &Env,
196 obj: &GObject,
197 version: Option<crate::version::Version>,
198 c_type: &str,
199 glib_get_type: &str,
200 functions: &[library::Function],
201) -> Result<()> {
202 let write_get_type = glib_get_type != INTERN;
203 if write_get_type || !functions.is_empty() {
204 writeln!(w)?;
205 writeln!(
206 w,
207 " //========================================================================="
208 )?;
209 writeln!(w, " // {c_type}")?;
210 writeln!(
211 w,
212 " //========================================================================="
213 )?;
214 }
215 if write_get_type {
216 let configured_functions = obj.functions.matched("get_type");
217
218 if configured_functions
219 .iter()
220 .all(|f| f.status.need_generate())
221 {
222 let version = std::iter::once(version)
223 .chain(configured_functions.iter().map(|f| f.version))
224 .max()
225 .flatten();
226 version_condition(w, env, None, version, false, 1)?;
227 generate_cfg_configure(w, &configured_functions, false)?;
228 writeln!(w, " pub fn {glib_get_type}() -> GType;")?;
229 }
230 }
231
232 for func in functions {
233 let configured_functions = obj.functions.matched(&func.name);
234 if !configured_functions
235 .iter()
236 .all(|f| f.status.need_generate())
237 {
238 continue;
239 }
240
241 let (commented, sig) = function_signature(env, func, false);
242 let comment = if commented { "//" } else { "" };
243
244 let version = configured_functions
248 .iter()
249 .map(|f| f.version)
250 .max()
251 .flatten()
252 .or(func.version)
253 .or(version);
254
255 version_condition(w, env, None, version, commented, 1)?;
256 let name = func.c_identifier.as_ref().unwrap();
257 generate_cfg_configure(w, &configured_functions, commented)?;
258 writeln!(w, " {comment}pub fn {name}{sig};")?;
259 }
260
261 Ok(())
262}
263
264pub fn generate_callbacks(
265 w: &mut dyn Write,
266 env: &Env,
267 callbacks: &[&library::Function],
268) -> Result<()> {
269 if !callbacks.is_empty() {
270 writeln!(w, "// Callbacks")?;
271 }
272 for func in callbacks {
273 let (commented, sig) = function_signature(env, func, true);
274 let comment = if commented { "//" } else { "" };
275 writeln!(
276 w,
277 "{}pub type {} = Option<unsafe extern \"C\" fn{}>;",
278 comment,
279 func.c_identifier.as_ref().unwrap(),
280 sig
281 )?;
282 }
283 if !callbacks.is_empty() {
284 writeln!(w)?;
285 }
286
287 Ok(())
288}
289
290pub fn function_signature(env: &Env, func: &library::Function, bare: bool) -> (bool, String) {
291 let (mut commented, ret_str) = function_return_value(env, func);
292
293 let mut parameter_strs: Vec<String> = Vec::new();
294 for par in &func.parameters {
295 let (c, par_str) = function_parameter(env, par, bare);
296 parameter_strs.push(par_str);
297 if c {
298 commented = true;
299 }
300 }
301
302 (
303 commented,
304 format!("({}){}", parameter_strs.join(", "), ret_str),
305 )
306}
307
308fn function_return_value(env: &Env, func: &library::Function) -> (bool, String) {
309 if func.ret.typ == Default::default() {
310 return (false, String::new());
311 }
312 let ffi_type = ffi_type(env, func.ret.typ, &func.ret.c_type);
313 let commented = ffi_type.is_err();
314 (commented, format!(" -> {}", ffi_type.into_string()))
315}
316
317fn function_parameter(env: &Env, par: &library::Parameter, bare: bool) -> (bool, String) {
318 if let library::Type::Basic(library::Basic::VarArgs) = env.library.type_(par.typ) {
319 return (false, "...".into());
320 }
321 let ffi_type = ffi_type(env, par.typ, &par.c_type);
322 let commented = ffi_type.is_err();
323 let res = if bare {
324 ffi_type.into_string()
325 } else {
326 format!(
327 "{}: {}",
328 nameutil::mangle_keywords(&*par.name),
329 ffi_type.into_string()
330 )
331 };
332 (commented, res)
333}