1use std::{
2 fmt::Write as FWrite,
3 io::{Result, Write},
4};
5
6use log::error;
7
8use super::{
9 return_value::ToReturnValue, trampoline_from_glib::TrampolineFromGlib,
10 trampoline_to_glib::TrampolineToGlib,
11};
12use crate::{
13 analysis::{
14 bounds::Bounds, ffi_type::ffi_type, ref_mode::RefMode, rust_type::RustType,
15 trampoline_parameters::*, trampolines::Trampoline, try_from_glib::TryFromGlib,
16 },
17 consts::TYPE_PARAMETERS_START,
18 env::Env,
19 library,
20 nameutil::{use_glib_if_needed, use_glib_type, use_gtk_type},
21 traits::IntoString,
22 writer::primitives::tabs,
23};
24
25pub fn generate(
26 w: &mut dyn Write,
27 env: &Env,
28 analysis: &Trampoline,
29 in_trait: bool,
30 indent: usize,
31) -> Result<()> {
32 let (self_bound, fn_self_bound) = in_trait
33 .then(|| {
34 (
35 format!("{}: IsA<{}>, ", TYPE_PARAMETERS_START, analysis.type_name),
36 Some(TYPE_PARAMETERS_START.to_string()),
37 )
38 })
39 .unwrap_or_default();
40
41 let prepend = tabs(indent);
42 let params_str = trampoline_parameters(env, analysis);
43 let func_str = func_string(env, analysis, fn_self_bound, true);
44 let ret_str = trampoline_returns(env, analysis);
45
46 writeln!(
47 w,
48 "{}unsafe extern \"C\" fn {}<{}F: {}>({}, f: {}){} {{",
49 prepend,
50 analysis.name,
51 self_bound,
52 func_str,
53 params_str,
54 use_glib_if_needed(env, "ffi::gpointer"),
55 ret_str,
56 )?;
57 writeln!(w, "{prepend}\tlet f: &F = &*(f as *const F);")?;
58 transformation_vars(w, env, analysis, &prepend)?;
59 let call = trampoline_call_func(env, analysis, in_trait);
60 writeln!(w, "{prepend}\t{call}")?;
61 writeln!(w, "{prepend}}}")?;
62
63 Ok(())
64}
65
66pub fn func_string(
67 env: &Env,
68 analysis: &Trampoline,
69 replace_self_bound: Option<impl AsRef<str>>,
70 closure: bool,
71) -> String {
72 let param_str = func_parameters(env, analysis, replace_self_bound, closure);
73 let return_str = func_returns(env, analysis);
74
75 if closure {
76 let concurrency_str = match analysis.concurrency {
77 library::Concurrency::Send => " + Send",
83 library::Concurrency::SendSync => " + Send + Sync",
87 library::Concurrency::None => "",
88 };
89
90 format!("Fn({param_str}){return_str}{concurrency_str} + 'static")
91 } else {
92 format!("({param_str}){return_str}",)
93 }
94}
95
96fn func_parameters(
97 env: &Env,
98 analysis: &Trampoline,
99 replace_self_bound: Option<impl AsRef<str>>,
100 closure: bool,
101) -> String {
102 let mut param_str = String::with_capacity(100);
103
104 for (pos, par) in analysis.parameters.rust_parameters.iter().enumerate() {
105 if pos == 0 {
106 if let Some(replace_self_bound) = &replace_self_bound {
107 param_str.push_str(par.ref_mode.for_rust_type());
108 param_str.push_str(replace_self_bound.as_ref());
109 continue;
110 }
111 } else {
112 param_str.push_str(", ");
113 if !closure {
114 write!(param_str, "{}: ", par.name).unwrap();
115 }
116 }
117
118 let s = func_parameter(env, par, &analysis.bounds);
119 param_str.push_str(&s);
120 }
121
122 param_str
123}
124
125fn func_parameter(env: &Env, par: &RustParameter, bounds: &Bounds) -> String {
126 let ref_mode = if par.ref_mode == RefMode::ByRefMut {
128 RefMode::ByRef
129 } else {
130 par.ref_mode
131 };
132
133 match bounds.get_parameter_bound(&par.name) {
134 Some(bound) => bound.full_type_parameter_reference(ref_mode, par.nullable, false),
136 None => RustType::builder(env, par.typ)
139 .direction(par.direction)
140 .nullable(par.nullable)
141 .ref_mode(ref_mode)
142 .try_build_param()
143 .into_string(),
144 }
145}
146
147fn func_returns(env: &Env, analysis: &Trampoline) -> String {
148 if analysis.ret.typ == Default::default() {
149 String::new()
150 } else if analysis.inhibit {
151 format!(" -> {inhibit}", inhibit = use_glib_type(env, "Propagation"))
152 } else if let Some(return_type) =
153 analysis
154 .ret
155 .to_return_value(env, &TryFromGlib::default(), true)
156 {
157 format!(" -> {return_type}")
158 } else {
159 String::new()
160 }
161}
162
163fn trampoline_parameters(env: &Env, analysis: &Trampoline) -> String {
164 if analysis.is_notify {
165 return format!(
166 "{}, _param_spec: {}",
167 trampoline_parameter(env, &analysis.parameters.c_parameters[0]),
168 use_glib_if_needed(env, "ffi::gpointer"),
169 );
170 }
171
172 let mut parameter_strs: Vec<String> = Vec::new();
173 for par in &analysis.parameters.c_parameters {
174 let par_str = trampoline_parameter(env, par);
175 parameter_strs.push(par_str);
176 }
177
178 parameter_strs.join(", ")
179}
180
181fn trampoline_parameter(env: &Env, par: &CParameter) -> String {
182 let ffi_type = ffi_type(env, par.typ, &par.c_type);
183 format!("{}: {}", par.name, ffi_type.into_string())
184}
185
186fn trampoline_returns(env: &Env, analysis: &Trampoline) -> String {
187 if analysis.ret.typ == Default::default() {
188 String::new()
189 } else {
190 let ffi_type = ffi_type(env, analysis.ret.typ, &analysis.ret.c_type);
191 format!(" -> {}", ffi_type.into_string())
192 }
193}
194
195fn transformation_vars(
196 w: &mut dyn Write,
197 env: &Env,
198 analysis: &Trampoline,
199 prepend: &str,
200) -> Result<()> {
201 use crate::analysis::trampoline_parameters::TransformationType::*;
202 for transform in &analysis.parameters.transformations {
203 match transform.transformation {
204 None => (),
205 Borrow => (),
206 TreePath => {
207 let c_par = &analysis.parameters.c_parameters[transform.ind_c];
208 writeln!(
209 w,
210 "{}\tlet {} = from_glib_full({}({}));",
211 prepend,
212 transform.name,
213 use_gtk_type(env, "ffi::gtk_tree_path_new_from_string"),
214 c_par.name
215 )?;
216 }
217 }
218 }
219 Ok(())
220}
221
222fn trampoline_call_func(env: &Env, analysis: &Trampoline, in_trait: bool) -> String {
223 let params = trampoline_call_parameters(env, analysis, in_trait);
224 let ret = if analysis.ret.typ == Default::default() {
225 String::new()
226 } else {
227 analysis.ret.trampoline_to_glib(env)
228 };
229 format!("f({params}){ret}")
230}
231
232fn trampoline_call_parameters(env: &Env, analysis: &Trampoline, in_trait: bool) -> String {
233 let mut need_downcast = in_trait;
234 let mut parameter_strs: Vec<String> = Vec::new();
235 for (ind, par) in analysis.parameters.rust_parameters.iter().enumerate() {
236 let transformation = match analysis.parameters.get(ind) {
237 Some(transformation) => transformation,
238 None => {
239 error!("No transformation for {}", par.name);
240 continue;
241 }
242 };
243 let par_str = transformation.trampoline_from_glib(env, need_downcast, *par.nullable);
244 parameter_strs.push(par_str);
245 need_downcast = false; }
247
248 parameter_strs.join(", ")
249}