libgir/codegen/
return_value.rs
1use std::cmp;
2
3use crate::{
4 analysis::{
5 self, conversion_type::ConversionType, namespaces, out_parameters::Mode,
6 rust_type::RustType, try_from_glib::TryFromGlib,
7 },
8 env::Env,
9 library::{self, ParameterDirection, TypeId},
10 nameutil::{is_gstring, mangle_keywords, use_glib_type},
11 traits::*,
12};
13
14pub trait ToReturnValue {
15 fn to_return_value(
16 &self,
17 env: &Env,
18 try_from_glib: &TryFromGlib,
19 is_trampoline: bool,
20 ) -> Option<String>;
21}
22
23impl ToReturnValue for library::Parameter {
24 fn to_return_value(
25 &self,
26 env: &Env,
27 try_from_glib: &TryFromGlib,
28 is_trampoline: bool,
29 ) -> Option<String> {
30 let mut name = RustType::builder(env, self.typ)
31 .direction(self.direction)
32 .nullable(self.nullable)
33 .scope(self.scope)
34 .try_from_glib(try_from_glib)
35 .try_build_param()
36 .into_string();
37 if is_trampoline
38 && self.direction == library::ParameterDirection::Return
39 && is_gstring(&name)
40 {
41 name = "String".to_owned();
42 }
43 let type_str = match ConversionType::of(env, self.typ) {
44 ConversionType::Unknown => format!("/*Unknown conversion*/{name}"),
45 _ => name,
47 };
48
49 Some(type_str)
50 }
51}
52
53impl ToReturnValue for analysis::return_value::Info {
54 fn to_return_value(
55 &self,
56 env: &Env,
57 try_from_glib: &TryFromGlib,
58 is_trampoline: bool,
59 ) -> Option<String> {
60 let par = self.parameter.as_ref()?;
61 par.lib_par
62 .to_return_value(env, try_from_glib, is_trampoline)
63 .map(|type_name| {
64 if self.nullable_return_is_error.is_some() && type_name.starts_with("Option<") {
65 format!(
67 "Result<{}, {}BoolError>",
68 &type_name[7..(type_name.len() - 1)],
69 if env.namespaces.glib_ns_id == namespaces::MAIN {
70 ""
71 } else {
72 "glib::"
73 }
74 )
75 } else {
76 type_name
77 }
78 })
79 }
80}
81
82pub fn out_parameter_types(analysis: &analysis::functions::Info) -> Vec<TypeId> {
84 if analysis.ret.bool_return_is_error.is_some()
86 || analysis.ret.nullable_return_is_error.is_some()
87 {
88 return Vec::new();
89 }
90
91 if !analysis.outs.is_empty() {
92 let num_out_args = analysis
93 .outs
94 .iter()
95 .filter(|out| out.lib_par.array_length.is_none())
96 .count();
97 let num_out_sizes = analysis
98 .outs
99 .iter()
100 .filter(|out| out.lib_par.array_length.is_some())
101 .count();
102 let num_outs = std::cmp::max(num_out_args, num_out_sizes);
108 match analysis.outs.mode {
109 Mode::Normal | Mode::Combined => {
110 let array_lengths: Vec<_> = analysis
111 .outs
112 .iter()
113 .filter_map(|out| out.lib_par.array_length)
114 .collect();
115 let mut ret_params = Vec::with_capacity(num_outs);
116
117 for out in analysis.outs.iter().filter(|out| !out.lib_par.is_error) {
118 if !out.lib_par.name.is_empty() {
120 let mangled_par_name =
121 crate::nameutil::mangle_keywords(out.lib_par.name.as_str());
122 let param_pos = analysis
123 .parameters
124 .c_parameters
125 .iter()
126 .enumerate()
127 .find_map(|(pos, orig_par)| {
128 if orig_par.name == mangled_par_name {
129 Some(pos)
130 } else {
131 None
132 }
133 })
134 .unwrap();
135 if array_lengths.contains(&(param_pos as u32)) {
136 continue;
137 }
138 }
139 ret_params.push(out.lib_par.typ);
140 }
141 ret_params
142 }
143 _ => Vec::new(),
144 }
145 } else if let Some(typ) = analysis.ret.parameter.as_ref().map(|out| out.lib_par.typ) {
146 vec![typ]
147 } else {
148 Vec::new()
149 }
150}
151
152fn out_parameter_as_return_parts(
153 analysis: &analysis::functions::Info,
154 env: &Env,
155) -> (&'static str, String) {
156 use crate::analysis::out_parameters::Mode::*;
157 let num_out_args = analysis
158 .outs
159 .iter()
160 .filter(|out| out.lib_par.array_length.is_none())
161 .count();
162 let num_out_sizes = analysis
163 .outs
164 .iter()
165 .filter(|out| out.lib_par.array_length.is_some())
166 .count();
167 let num_outs = cmp::max(num_out_args, num_out_sizes);
172 match analysis.outs.mode {
173 Normal | Combined => {
174 if num_outs > 1 {
175 ("(", ")".to_owned())
176 } else {
177 ("", String::new())
178 }
179 }
180 Optional => {
181 if num_outs > 1 {
182 if analysis.ret.nullable_return_is_error.is_some() {
183 (
184 "Result<(",
185 format!("), {}>", use_glib_type(env, "BoolError")),
186 )
187 } else {
188 ("Option<(", ")>".to_owned())
189 }
190 } else if analysis.ret.nullable_return_is_error.is_some() {
191 ("Result<", format!(", {}>", use_glib_type(env, "BoolError")))
192 } else {
193 ("Option<", ">".to_owned())
194 }
195 }
196 Throws(..) => {
197 if num_outs == 1 + 1 {
198 ("Result<", format!(", {}>", use_glib_type(env, "Error")))
200 } else {
201 ("Result<(", format!("), {}>", use_glib_type(env, "Error")))
202 }
203 }
204 None => unreachable!(),
205 }
206}
207
208pub fn out_parameters_as_return(env: &Env, analysis: &analysis::functions::Info) -> String {
209 let (prefix, suffix) = out_parameter_as_return_parts(analysis, env);
210 let mut return_str = String::with_capacity(100);
211 return_str.push_str(" -> ");
212 return_str.push_str(prefix);
213
214 let array_lengths: Vec<_> = analysis
215 .outs
216 .iter()
217 .filter_map(|out| out.lib_par.array_length)
218 .collect();
219
220 let mut skip = 0;
221 for (pos, out) in analysis
222 .outs
223 .iter()
224 .filter(|out| !out.lib_par.is_error)
225 .enumerate()
226 {
227 if !out.lib_par.name.is_empty() {
229 let mangled_par_name = mangle_keywords(out.lib_par.name.as_str());
230 let param_pos = analysis
231 .parameters
232 .c_parameters
233 .iter()
234 .enumerate()
235 .find_map(|(pos, orig_par)| {
236 if orig_par.name == mangled_par_name {
237 Some(pos)
238 } else {
239 None
240 }
241 })
242 .unwrap();
243 if array_lengths.contains(&(param_pos as u32)) {
244 skip += 1;
245 continue;
246 }
247 }
248
249 if pos > skip {
250 return_str.push_str(", ");
251 }
252 let s = out_parameter_as_return(out, env);
253 return_str.push_str(&s);
254 }
255 return_str.push_str(&suffix);
256 return_str
257}
258
259fn out_parameter_as_return(out: &analysis::Parameter, env: &Env) -> String {
260 let name = RustType::builder(env, out.lib_par.typ)
262 .direction(ParameterDirection::Return)
263 .nullable(out.lib_par.nullable)
264 .scope(out.lib_par.scope)
265 .try_from_glib(&out.try_from_glib)
266 .try_build_param()
267 .into_string();
268 match ConversionType::of(env, out.lib_par.typ) {
269 ConversionType::Unknown => format!("/*Unknown conversion*/{name}"),
270 _ => name,
271 }
272}