1use std::{
2 fmt,
3 io::{Result, Write},
4 result::Result as StdResult,
5};
6
7use log::warn;
8
9use super::{
10 function_body_chunk,
11 general::{
12 allow_deprecated, cfg_condition, cfg_deprecated, doc_alias, doc_hidden,
13 not_version_condition, version_condition,
14 },
15 parameter::ToParameter,
16 return_value::{out_parameter_types, out_parameters_as_return, ToReturnValue},
17 special_functions,
18};
19use crate::{
20 analysis::{self, bounds::Bounds, try_from_glib::TryFromGlib},
21 chunk::{ffi_function_todo, Chunk},
22 env::Env,
23 library::{self, TypeId},
24 nameutil::use_glib_type,
25 version::Version,
26 writer::{primitives::tabs, ToCode},
27};
28
29pub fn get_must_use_if_needed(
34 parent_type_id: Option<TypeId>,
35 analysis: &analysis::functions::Info,
36 comment_prefix: &str,
37) -> Option<String> {
38 if let Some(parent_type_id) = parent_type_id {
41 if analysis.kind == library::FunctionKind::Method {
44 let outs = out_parameter_types(analysis);
46 if [parent_type_id] == *outs.as_slice() {
49 return Some(format!("{comment_prefix}#[must_use]\n"));
50 }
51 }
52 }
53 None
54}
55
56pub fn generate(
57 w: &mut dyn Write,
58 env: &Env,
59 parent_type_id: Option<TypeId>,
60 analysis: &analysis::functions::Info,
61 special_functions: Option<&analysis::special_functions::Infos>,
62 scope_version: Option<Version>,
63 in_trait: bool,
64 only_declaration: bool,
65 indent: usize,
66) -> Result<()> {
67 if !analysis.status.need_generate() {
68 return Ok(());
69 }
70
71 if analysis.is_async_finish(env) {
72 return Ok(());
73 }
74
75 if let Some(special_functions) = special_functions {
76 if special_functions::generate(w, env, analysis, special_functions, scope_version)? {
77 return Ok(());
78 }
79 }
80
81 if analysis.hidden {
82 return Ok(());
83 }
84
85 let commented = analysis.commented;
86 let comment_prefix = if commented { "//" } else { "" };
87 let pub_prefix = if in_trait {
88 String::new()
89 } else {
90 format!("{} ", analysis.visibility)
91 };
92
93 let unsafe_ = if analysis.unsafe_ { "unsafe " } else { "" };
94 let declaration = declaration(env, analysis);
95 let suffix = if only_declaration { ";" } else { " {" };
96
97 writeln!(w)?;
98 cfg_deprecated(w, env, None, analysis.deprecated_version, commented, indent)?;
99 cfg_condition(w, analysis.cfg_condition.as_ref(), commented, indent)?;
100 let version = Version::if_stricter_than(analysis.version, scope_version);
101 version_condition(w, env, None, version, commented, indent)?;
102 not_version_condition(w, analysis.not_version, commented, indent)?;
103 doc_hidden(w, analysis.doc_hidden, comment_prefix, indent)?;
104 allow_deprecated(w, analysis.deprecated_version, commented, indent)?;
105 doc_alias(w, &analysis.glib_name, comment_prefix, indent)?;
106 if analysis.codegen_name() != analysis.func_name {
107 doc_alias(w, &analysis.func_name, comment_prefix, indent)?;
108 }
109 if let Some(get_property) = &analysis.get_property {
112 if get_property != analysis.codegen_name() {
113 doc_alias(w, get_property, comment_prefix, indent)?;
114 }
115 } else if let Some(set_property) = &analysis.set_property {
116 if set_property != analysis.codegen_name() {
117 doc_alias(w, set_property, comment_prefix, indent)?;
118 }
119 }
120 let dead_code_cfg = if !analysis.visibility.is_public() && !analysis.is_special() {
122 "#[allow(dead_code)]"
123 } else {
124 ""
125 };
126
127 let allow_should_implement_trait = if analysis.codegen_name() == "default" {
128 format!(
129 "{}{}#[allow(clippy::should_implement_trait)]",
130 tabs(indent),
131 comment_prefix
132 )
133 } else {
134 String::new()
135 };
136
137 writeln!(
138 w,
139 "{}{}{}{}{}{}{}{}{}",
140 allow_should_implement_trait,
141 dead_code_cfg,
142 get_must_use_if_needed(parent_type_id, analysis, comment_prefix).unwrap_or_default(),
143 tabs(indent),
144 comment_prefix,
145 pub_prefix,
146 unsafe_,
147 declaration,
148 suffix,
149 )?;
150
151 if !only_declaration {
152 let body = body_chunk(env, analysis, parent_type_id).to_code(env);
153 for s in body {
154 writeln!(w, "{}{}", tabs(indent), s)?;
155 }
156 }
157
158 if analysis.async_future.is_some() {
159 let declaration = declaration_futures(env, analysis);
160 let suffix = if only_declaration { ";" } else { " {" };
161
162 writeln!(w)?;
163 cfg_deprecated(w, env, None, analysis.deprecated_version, commented, indent)?;
164
165 writeln!(w, "{}{}", tabs(indent), comment_prefix)?;
166 cfg_condition(w, analysis.cfg_condition.as_ref(), commented, indent)?;
167 version_condition(w, env, None, version, commented, indent)?;
168 not_version_condition(w, analysis.not_version, commented, indent)?;
169 doc_hidden(w, analysis.doc_hidden, comment_prefix, indent)?;
170 writeln!(
171 w,
172 "{}{}{}{}{}{}",
173 tabs(indent),
174 comment_prefix,
175 pub_prefix,
176 unsafe_,
177 declaration,
178 suffix
179 )?;
180
181 if !only_declaration {
182 let body = body_chunk_futures(env, analysis).unwrap();
183 for s in body.lines() {
184 if !s.is_empty() {
185 writeln!(w, "{}{}{}", tabs(indent + 1), comment_prefix, s)?;
186 } else {
187 writeln!(w)?;
188 }
189 }
190 writeln!(w, "{}{}}}", tabs(indent), comment_prefix)?;
191 }
192 }
193
194 Ok(())
195}
196
197pub fn declaration(env: &Env, analysis: &analysis::functions::Info) -> String {
198 let outs_as_return = !analysis.outs.is_empty();
199 let return_str = if outs_as_return {
200 out_parameters_as_return(env, analysis)
201 } else if analysis.ret.bool_return_is_error.is_some() {
202 format!(" -> Result<(), {}>", use_glib_type(env, "error::BoolError"))
203 } else if let Some(return_type) = analysis.ret.to_return_value(
204 env,
205 analysis
206 .ret
207 .parameter
208 .as_ref()
209 .map_or(&TryFromGlib::Default, |par| &par.try_from_glib),
210 false,
211 ) {
212 format!(" -> {return_type}")
213 } else {
214 String::new()
215 };
216 let mut param_str = String::with_capacity(100);
217
218 let (bounds, _) = bounds(&analysis.bounds, &[], false, false);
219
220 for par in &analysis.parameters.rust_parameters {
221 if !param_str.is_empty() {
222 param_str.push_str(", ");
223 }
224 let c_par = &analysis.parameters.c_parameters[par.ind_c];
225 let s = c_par.to_parameter(env, &analysis.bounds, false);
226 param_str.push_str(&s);
227 }
228
229 format!(
230 "fn {}{}({}){}",
231 analysis.codegen_name(),
232 bounds,
233 param_str,
234 return_str,
235 )
236}
237
238pub fn declaration_futures(env: &Env, analysis: &analysis::functions::Info) -> String {
239 let async_future = analysis.async_future.as_ref().unwrap();
240
241 let return_str = if let Some(ref error_parameters) = async_future.error_parameters {
242 format!(
243 " -> Pin<Box_<dyn std::future::Future<Output = Result<{}, {}>> + 'static>>",
244 async_future.success_parameters, error_parameters
245 )
246 } else {
247 format!(
248 " -> Pin<Box_<dyn std::future::Future<Output = {}> + 'static>>",
249 async_future.success_parameters
250 )
251 };
252
253 let mut param_str = String::with_capacity(100);
254
255 let mut skipped = 0;
256 let mut skipped_bounds = vec![];
257 for (pos, par) in analysis.parameters.rust_parameters.iter().enumerate() {
258 let c_par = &analysis.parameters.c_parameters[par.ind_c];
259
260 if c_par.name == "callback" || c_par.name == "cancellable" {
261 skipped += 1;
262 if let Some(alias) = analysis
263 .bounds
264 .get_parameter_bound(&c_par.name)
265 .and_then(|bound| bound.type_parameter_reference())
266 {
267 skipped_bounds.push(alias);
268 }
269 continue;
270 }
271
272 if pos - skipped > 0 {
273 param_str.push_str(", ");
274 }
275
276 let s = c_par.to_parameter(env, &analysis.bounds, true);
277 param_str.push_str(&s);
278 }
279
280 let (bounds, _) = bounds(&analysis.bounds, skipped_bounds.as_ref(), true, false);
281
282 format!(
283 "fn {}{}({}){}",
284 async_future.name, bounds, param_str, return_str,
285 )
286}
287
288pub fn bounds(
289 bounds: &Bounds,
290 skip: &[char],
291 r#async: bool,
292 filter_callback_modified: bool,
293) -> (String, Vec<String>) {
294 use crate::analysis::bounds::BoundType::*;
295
296 if bounds.is_empty() {
297 return (String::new(), Vec::new());
298 }
299
300 let skip_lifetimes = bounds
301 .iter()
302 .filter(|bound| bound.alias.is_some_and(|alias| skip.contains(&alias)))
304 .filter_map(|bound| match bound.bound_type {
305 IsA(lifetime) | AsRef(lifetime) => lifetime,
306 _ => None,
307 })
308 .collect::<Vec<_>>();
309
310 let lifetimes = bounds
311 .iter_lifetimes()
312 .filter(|s| !skip_lifetimes.contains(s))
313 .map(|s| format!("'{s}"))
314 .collect::<Vec<_>>();
315
316 let bounds = bounds.iter().filter(|bound| {
317 bound.alias.is_none_or(|alias| !skip.contains(&alias))
318 && (!filter_callback_modified || !bound.callback_modified)
319 });
320
321 let type_names = lifetimes
322 .iter()
323 .cloned()
324 .chain(
325 bounds
326 .clone()
327 .filter_map(|b| b.type_parameter_definition(r#async)),
328 )
329 .collect::<Vec<_>>();
330
331 let type_names = if type_names.is_empty() {
332 String::new()
333 } else {
334 format!("<{}>", type_names.join(", "))
335 };
336
337 let bounds = lifetimes
338 .into_iter()
339 .chain(bounds.filter_map(|b| b.alias).map(|a| a.to_string()))
342 .collect::<Vec<_>>();
343
344 (type_names, bounds)
345}
346
347pub fn body_chunk(
348 env: &Env,
349 analysis: &analysis::functions::Info,
350 parent_type_id: Option<TypeId>,
351) -> Chunk {
352 if analysis.commented {
353 return ffi_function_todo(env, &analysis.glib_name);
354 }
355
356 let outs_as_return = !analysis.outs.is_empty();
357
358 let mut builder = function_body_chunk::Builder::new();
359 let sys_crate_name = if let Some(ty_id) = parent_type_id {
360 env.sys_crate_import(ty_id)
361 } else {
362 env.main_sys_crate_name().to_owned()
363 };
364
365 builder
366 .glib_name(&format!("{}::{}", sys_crate_name, analysis.glib_name))
367 .assertion(analysis.assertion)
368 .ret(&analysis.ret)
369 .transformations(&analysis.parameters.transformations)
370 .in_unsafe(analysis.unsafe_)
371 .outs_mode(analysis.outs.mode);
372
373 if analysis.r#async {
374 if let Some(ref trampoline) = analysis.trampoline {
375 builder.async_trampoline(trampoline);
376 } else {
377 warn!(
378 "Async function {} has no associated _finish function",
379 analysis.codegen_name(),
380 );
381 }
382 } else {
383 for trampoline in &analysis.callbacks {
384 builder.callback(trampoline);
385 }
386 for trampoline in &analysis.destroys {
387 builder.destroy(trampoline);
388 }
389 }
390
391 for par in &analysis.parameters.c_parameters {
392 if outs_as_return && analysis.outs.iter().any(|out| out.lib_par.name == par.name) {
393 builder.out_parameter(env, par);
394 } else {
395 builder.parameter();
396 }
397 }
398
399 let (bounds, bounds_names) = bounds(&analysis.bounds, &[], false, true);
400
401 builder.generate(env, &bounds, &bounds_names.join(", "))
402}
403
404pub fn body_chunk_futures(
405 env: &Env,
406 analysis: &analysis::functions::Info,
407) -> StdResult<String, fmt::Error> {
408 use std::fmt::Write;
409
410 use crate::analysis::ref_mode::RefMode;
411
412 let async_future = analysis.async_future.as_ref().unwrap();
413
414 let mut body = String::new();
415
416 let gio_future_name = if env.config.library_name != "Gio" {
417 "gio::GioFuture"
418 } else {
419 "crate::GioFuture"
420 };
421 writeln!(body)?;
422
423 if !async_future.assertion.is_none() {
424 writeln!(body, "{}", async_future.assertion)?;
425 }
426 let skip = usize::from(async_future.is_method);
427
428 for par in analysis.parameters.rust_parameters.iter().skip(skip) {
430 if par.name == "cancellable" || par.name == "callback" {
431 continue;
432 }
433
434 let c_par = &analysis.parameters.c_parameters[par.ind_c];
435
436 let type_ = env.type_(par.typ);
437 let is_str = matches!(type_, library::Type::Basic(library::Basic::Utf8));
438 let is_slice = matches!(type_, library::Type::CArray(_));
439
440 if is_slice {
441 writeln!(body, "let {} = {}.to_vec();", par.name, par.name)?;
442 } else if *c_par.nullable {
443 writeln!(
444 body,
445 "let {} = {}.map(ToOwned::to_owned);",
446 par.name, par.name
447 )?;
448 } else if is_str {
449 writeln!(body, "let {} = String::from({});", par.name, par.name)?;
450 } else if c_par.ref_mode != RefMode::None {
451 writeln!(body, "let {} = {}.clone();", par.name, par.name)?;
452 }
453 }
454
455 if async_future.is_method {
456 writeln!(
457 body,
458 "Box_::pin({gio_future_name}::new(self, move |obj, cancellable, send| {{"
459 )?;
460 } else {
461 writeln!(
462 body,
463 "Box_::pin({gio_future_name}::new(&(), move |_obj, cancellable, send| {{"
464 )?;
465 }
466
467 if async_future.is_method {
468 writeln!(body, "\tobj.{}(", analysis.codegen_name())?;
469 } else if analysis.type_name.is_ok() {
470 writeln!(body, "\tSelf::{}(", analysis.codegen_name())?;
471 } else {
472 writeln!(body, "\t{}(", analysis.codegen_name())?;
473 }
474
475 for par in analysis.parameters.rust_parameters.iter().skip(skip) {
477 if par.name == "cancellable" {
478 writeln!(body, "\t\tSome(cancellable),")?;
479 } else if par.name == "callback" {
480 continue;
481 } else {
482 let c_par = &analysis.parameters.c_parameters[par.ind_c];
483
484 if *c_par.nullable {
485 writeln!(
486 body,
487 "\t\t{}.as_ref().map(::std::borrow::Borrow::borrow),",
488 par.name
489 )?;
490 } else if c_par.ref_mode != RefMode::None {
491 writeln!(body, "\t\t&{},", par.name)?;
492 } else {
493 writeln!(body, "\t\t{},", par.name)?;
494 }
495 }
496 }
497
498 writeln!(body, "\t\tmove |res| {{")?;
499 writeln!(body, "\t\t\tsend.resolve(res);")?;
500 writeln!(body, "\t\t}},")?;
501 writeln!(body, "\t);")?;
502 writeln!(body, "}}))")?;
503
504 Ok(body)
505}