1use std::borrow::Cow;
2use std::collections::HashMap;
3use std::fmt::Write;
4use std::iter;
5use std::iter::{Chain, Once};
6use std::rc::Rc;
7use std::vec::IntoIter;
8
9use once_cell::sync::Lazy;
10use Cow::{Borrowed, Owned};
11
12use super::comment::{render_ref, RenderComment};
13use super::element::{DefaultRustNativeElement, RustElement};
14use super::type_ref::{Lifetime, TypeRefExt};
15use super::{comment, rust_disambiguate_names, RustNativeGeneratedElement};
16use crate::field::Field;
17use crate::func::{FuncCppBody, FuncKind, FuncRustBody, FuncRustExtern, InheritConfig, OperatorKind, ReturnKind};
18use crate::name_pool::NamePool;
19use crate::settings::ARG_OVERRIDE_SELF;
20use crate::type_ref::{Constness, CppNameStyle, ExternDir, FishStyle, NameStyle, StrEnc, StrType, TypeRef, TypeRefTypeHint};
21use crate::writer::rust_native::type_ref::render_lane::FunctionProps;
22use crate::{reserved_rename, CompiledInterpolation, Element, Func, IteratorExt, NameDebug, StrExt, StringExt};
23
24pub trait FuncExt<'tu, 'ge> {
25 fn companion_functions(&self) -> Vec<Func<'tu, 'ge>>;
26 fn with_companion_functions(self) -> Chain<Once<Self>, IntoIter<Self>>
27 where
28 Self: Sized;
29}
30
31impl<'tu, 'ge> FuncExt<'tu, 'ge> for Func<'tu, 'ge> {
32 fn companion_functions(&self) -> Vec<Func<'tu, 'ge>> {
33 let mut out = vec![];
34 if let Some(default_func) = companion_func_default_args(self) {
35 out.extend(default_func.companion_functions());
36 out.push(default_func);
37 }
38 out.extend(companion_func_boxref_mut(self));
39 out
40 }
41
42 fn with_companion_functions(self) -> Chain<Once<Self>, IntoIter<Self>> {
43 let companions = self.companion_functions();
44 iter::once(self).chain(companions)
45 }
46}
47
48impl RustElement for Func<'_, '_> {
49 fn rust_module(&self) -> Cow<str> {
50 match self {
51 &Self::Clang { entity, .. } => DefaultRustNativeElement::rust_module(entity),
52 Self::Desc(desc) => desc.rust_module.as_ref().into(),
53 }
54 }
55
56 fn rust_name(&self, style: NameStyle) -> Cow<str> {
57 match self {
58 &Self::Clang { entity, .. } => DefaultRustNativeElement::rust_name(self, entity, style).into(),
59 Self::Desc(_) => match style {
60 NameStyle::Declaration => self.rust_leafname(FishStyle::No),
61 NameStyle::Reference(fish_style) => format!(
62 "{}::{}",
63 DefaultRustNativeElement::rust_module_reference(self),
64 self.rust_leafname(fish_style)
65 )
66 .into(),
67 },
68 }
69 }
70
71 fn rust_leafname(&self, _fish_style: FishStyle) -> Cow<str> {
72 if let Some(rust_custom_leafname) = self.rust_custom_leafname() {
73 return rust_custom_leafname.into();
74 }
75 let cpp_name = match self {
76 &Self::Clang { entity, gen_env, .. } => {
77 if let Some(name) = gen_env.get_rename_config(entity).map(|c| &c.rename) {
78 Borrowed(name.as_ref())
79 } else {
80 self.cpp_name(CppNameStyle::Declaration)
81 }
82 }
83 Self::Desc(_) => self.cpp_name(CppNameStyle::Declaration),
84 };
85 let rust_name = if self.is_clone() {
86 Borrowed("try_clone")
87 } else {
88 let kind = self.kind();
89 if let Some(cls) = kind.as_constructor() {
90 let args = self.arguments();
91 let ctor_name = 'ctor_name: {
92 if args.is_empty() {
93 break 'ctor_name "default";
94 } else if args.len() == 1 {
95 let arg_typeref = args[0].type_ref();
96 let source = arg_typeref.source_smart();
97 if let Some(class_arg) = source.kind().as_class() {
98 if cls == class_arg.as_ref() {
99 break 'ctor_name if arg_typeref.constness().is_const() {
100 "copy"
101 } else {
102 "copy_mut"
103 };
104 } else if class_arg.descendants().contains(cls) {
105 break 'ctor_name "from_base";
106 }
107 }
108 }
109 "new"
110 };
111 Borrowed(ctor_name)
112 } else if kind.as_conversion_method().is_some() {
113 let mut conv_name = self.return_type_ref().rust_name(NameStyle::decl()).into_owned();
114 conv_name.cleanup_name();
115 conv_name.insert_str(0, "to_");
116 Owned(conv_name)
117 } else if let Some((cls, kind)) = kind.as_operator() {
118 if cpp_name.starts_with("operator") {
119 let op_name = match kind {
120 OperatorKind::Unsupported => cpp_name.as_ref(),
121 OperatorKind::Index => {
122 if self.constness().is_const() {
123 "get"
124 } else {
125 "get_mut"
126 }
127 }
128 OperatorKind::Add => "add",
129 OperatorKind::Sub => "sub",
130 OperatorKind::Mul => "mul",
131 OperatorKind::Div => "div",
132 OperatorKind::Apply => "apply",
133 OperatorKind::Set => "set",
134 OperatorKind::Deref => {
135 if self.constness().is_const() {
136 "try_deref"
137 } else {
138 "try_deref_mut"
139 }
140 }
141 OperatorKind::Equals => "equals",
142 OperatorKind::NotEquals => "not_equals",
143 OperatorKind::GreaterThan => "greater_than",
144 OperatorKind::GreaterThanOrEqual => "greater_than_or_equal",
145 OperatorKind::LessThan => "less_than",
146 OperatorKind::LessThanOrEqual => "less_than_or_equal",
147 OperatorKind::Incr => "incr",
148 OperatorKind::Decr => "decr",
149 OperatorKind::And => "and",
150 OperatorKind::Or => "or",
151 OperatorKind::Xor => "xor",
152 OperatorKind::BitwiseNot => "negate",
153 };
154 if kind.add_args_to_name() {
155 let args = self.arguments();
156 let args = args.as_ref();
157 let is_single_arg_same_as_class = if let (Some(cls), [single_arg]) = (cls, args) {
158 single_arg
159 .type_ref()
160 .source()
161 .kind()
162 .as_class()
163 .is_some_and(|single_class| single_class.as_ref() == cls)
164 } else {
165 false
166 };
167 if args.is_empty() || is_single_arg_same_as_class {
168 Borrowed(op_name)
169 } else {
170 let args = args.iter().map(|arg| arg.type_ref().rust_simple_name()).join("_");
171 Owned(format!("{op_name}_{args}"))
172 }
173 } else {
174 Borrowed(op_name)
175 }
176 } else {
177 cpp_name
178 }
179 } else {
180 cpp_name
181 }
182 };
183 let rust_name = reserved_rename(rust_name.cpp_name_to_rust_fn_case());
184 if let Self::Clang { gen_env, .. } = self {
185 if let Some(&name) = gen_env.settings.func_rename.get(self.identifier().as_str()) {
186 return if name.contains('+') {
187 Owned(name.replacen('+', rust_name.as_ref(), 1))
188 } else {
189 name.into()
190 };
191 }
192 }
193 Owned(rust_name.into_owned())
194 }
195
196 fn rendered_doc_comment(&self, comment_marker: &str, opencv_version: &str) -> String {
197 let mut comment = RenderComment::new(&self.doc_comment_overloaded(), opencv_version);
198 let args = self.arguments();
199 let (_, default_args) = split_default_args(&args);
200 let default_args_comment = comment::render_cpp_default_args(default_args);
201 if !default_args_comment.is_empty() {
202 if !comment.comment.is_empty() {
203 comment.comment.push_str("\n\n");
204 }
205 comment.comment.push_str("## C++ default parameters\n");
206 comment.comment.push_str(default_args_comment.trim_end());
207 }
208 comment.render_with_comment_marker(comment_marker).into_owned()
209 }
210}
211
212impl RustNativeGeneratedElement for Func<'_, '_> {
213 fn element_safe_id(&self) -> String {
214 format!("{}-{}", self.rust_module(), self.rust_name(NameStyle::decl()))
215 }
216
217 fn gen_rust(&self, opencv_version: &str) -> String {
218 static TPL: Lazy<CompiledInterpolation> = Lazy::new(|| include_str!("tpl/func/rust.tpl.rs").compile_interpolation());
219
220 let name = self.rust_leafname(FishStyle::No);
221 let kind = self.kind();
222 let return_kind = self.return_kind();
223 let return_type_ref = self.return_type_ref();
224 let safety = self.safety();
225 let identifier = self.identifier();
226
227 let args: Vec<(String, Field)> = rust_disambiguate_names(self.arguments().into_owned()).collect::<Vec<_>>();
228 let as_instance_method = kind.as_instance_method();
229 let mut decl_args = Vec::with_capacity(args.len());
230 let mut pre_call_args = Vec::with_capacity(args.len());
231 let mut call_args = Vec::with_capacity(args.len() + 1);
232 let mut forward_args = Vec::with_capacity(args.len());
233 let mut post_success_call_args = Vec::with_capacity(args.len());
234 let boxed_ref_arg = return_type_ref.type_hint().as_boxed_as_ref();
235 if let Some(cls) = as_instance_method {
236 let constness = self.constness();
237 let cls_type_ref = cls.type_ref().with_inherent_constness(constness);
238 let render_lane = cls_type_ref.render_lane();
239 let render_lane = render_lane.to_dyn();
240 let lt = boxed_ref_arg
241 .filter(|(_, boxed_arg_names, _)| boxed_arg_names.contains(&ARG_OVERRIDE_SELF))
242 .map_or(Lifetime::Elided, |(_, _, lt)| lt);
243 decl_args.push(render_lane.rust_self_func_decl(lt));
244 call_args.push(render_lane.rust_arg_func_call("self"));
245 }
246 let mut callback_arg_name: Option<&str> = None;
247 let function_props = FunctionProps {
248 is_infallible: return_kind.is_infallible(),
249 };
250 for (name, arg) in &args {
251 let arg_type_ref = arg.type_ref();
252 let arg_type_hint = arg_type_ref.type_hint();
253 let arg_as_slice_len = arg_type_hint.as_slice_len();
254 let arg_kind = arg_type_ref.kind();
255 let render_lane = arg_type_ref.render_lane();
256 let render_lane = render_lane.to_dyn();
257 if arg.is_user_data() {
258 pre_post_arg_handle(
259 format!(
260 "userdata_arg!({decl} => {callback_name})",
261 decl = arg_type_ref.render_lane().to_dyn().rust_extern_arg_func_decl(name),
262 callback_name = callback_arg_name.expect("Can't get name of the callback arg")
263 ),
264 &mut pre_call_args,
265 );
266 } else {
267 if arg_kind.is_function() {
268 callback_arg_name = Some(name);
269 }
270 if !arg_as_slice_len.is_some() {
271 let lt = boxed_ref_arg
272 .filter(|(_, boxed_arg_names, _)| boxed_arg_names.contains(&name.as_str()))
273 .map(|(_, _, lt)| lt)
274 .or_else(|| arg_type_hint.as_explicit_lifetime())
275 .unwrap_or(Lifetime::Elided);
276 decl_args.push(render_lane.rust_arg_func_decl(name, lt).into());
277 }
278 pre_post_arg_handle(render_lane.rust_arg_pre_call(name, &function_props), &mut pre_call_args);
279 }
280 if let Some((slice_args, len_div)) = arg_as_slice_len {
281 let arg_is_size_t = arg_kind.is_size_t();
282 let mut slice_len_call = String::new();
283 for slice_arg in slice_args {
284 let len_divided = if len_div > 1 {
285 format!("{slice_arg}.len() / {len_div}")
286 } else {
287 format!("{slice_arg}.len()")
288 };
289 if slice_len_call.is_empty() {
290 if len_div > 1 && (!arg_is_size_t || slice_args.len() > 1) {
291 write!(&mut slice_len_call, "({len_divided})").expect("Impossible");
292 } else {
293 slice_len_call.push_str(&len_divided);
294 }
295 } else {
296 write!(&mut slice_len_call, ".min({len_divided})").expect("Impossible");
297 }
298 }
299 if !arg_is_size_t {
300 slice_len_call.push_str(".try_into()?");
301 }
302 call_args.push(slice_len_call);
303 } else {
304 call_args.push(render_lane.rust_arg_func_call(name));
305 }
306 forward_args.push(name.as_str());
307 pre_post_arg_handle(render_lane.rust_arg_post_success_call(name), &mut post_success_call_args);
308 }
309 if !return_kind.is_naked() {
310 call_args.push("ocvrs_return.as_mut_ptr()".to_string());
311 }
312
313 let doc_comment = self.rendered_doc_comment("///", opencv_version);
314 let visibility = if let Some(cls) = as_instance_method {
315 if cls.kind().is_trait() {
316 ""
317 } else {
318 "pub "
319 }
320 } else {
321 "pub "
322 };
323 let return_lifetime = match boxed_ref_arg {
324 Some((_, _, lifetime)) => lifetime,
325 None if matches!(kind.as_ref(), FuncKind::Function | FuncKind::StaticMethod(_)) => Lifetime::statik(),
326 None => Lifetime::Elided,
327 };
328 let mut return_type_func_decl = return_type_ref.rust_return(FishStyle::No, return_lifetime);
329 if !return_kind.is_infallible() {
330 return_type_func_decl = format!("Result<{return_type_func_decl}>").into()
331 };
332 let return_type_func_decl = if return_type_func_decl == "()" {
333 "".to_string()
334 } else {
335 format!(" -> {return_type_func_decl}")
336 };
337 let (ret_pre_call, ret_handle, ret_stmt) = rust_return(self, &return_type_ref, return_kind, return_lifetime);
338 let mut attributes = Vec::with_capacity(2);
339 if self.is_no_discard() {
340 attributes.push(Borrowed("#[must_use]"));
341 }
342 if let Some((rust_attr, _)) = self.cfg_attrs() {
343 if !rust_attr.is_empty() {
344 attributes.push(format!("#[cfg({rust_attr})]").into());
345 }
346 }
347
348 TPL.interpolate(&HashMap::from([
349 ("doc_comment", doc_comment.as_str()),
350 ("debug", &self.get_debug()),
351 ("attributes", &attributes.join("\n")),
352 ("visibility", visibility),
353 ("unsafety_decl", safety.rust_func_safety_qual()),
354 ("name", name.as_ref()),
355 ("generic_decl", &rust_generic_decl(self, &return_type_ref)),
356 ("decl_args", &decl_args.join(", ")),
357 ("rv_rust_full", &return_type_func_decl),
358 ("pre_call_args", &pre_call_args.join("\n")),
359 ("return_pre_call", ret_pre_call),
360 (
361 "call",
362 &rust_call(self, &identifier, &name, &call_args, &forward_args, return_kind),
363 ),
364 ("return_handle", &ret_handle),
365 ("post_success_call_args", &post_success_call_args.join("\n")),
366 ("return", ret_stmt),
367 ]))
368 }
369
370 fn gen_rust_externs(&self) -> String {
371 static TPL: Lazy<CompiledInterpolation> = Lazy::new(|| include_str!("tpl/func/rust_extern.tpl.rs").compile_interpolation());
372
373 if matches!(self.rust_extern_definition(), FuncRustExtern::Absent) {
374 return "".to_string();
375 }
376
377 let identifier = self.identifier();
378 let mut attributes = String::new();
379 if let Some((rust_attr, _)) = self.cfg_attrs() {
380 attributes = format!("#[cfg({rust_attr})]");
381 }
382 let mut args = vec![];
383 if let Some(cls) = self.kind().as_instance_method() {
384 args.push(
385 cls.type_ref()
386 .with_inherent_constness(self.constness())
387 .render_lane()
388 .to_dyn()
389 .rust_extern_arg_func_decl("instance"),
390 );
391 }
392 for (name, arg) in rust_disambiguate_names(self.arguments().into_owned()) {
393 args.push(arg.type_ref().render_lane().to_dyn().rust_extern_arg_func_decl(&name))
394 }
395
396 let return_kind = self.return_kind();
397 let naked_return = return_kind.is_naked();
398 let is_infallible = return_kind.is_infallible();
399 let return_type = self.return_type_ref();
400 let return_wrapper_type = if is_infallible {
401 return_type.rust_extern(ExternDir::FromCpp)
402 } else {
403 return_type.rust_extern_return_fallible()
404 };
405 if !naked_return {
406 let ret_name = "ocvrs_return";
407 args.push(format!("{ret_name}: *mut {return_wrapper_type}"));
408 }
409 let return_type_kind = return_type.kind();
410 let return_wrapper_type = if return_type_kind.is_void() || !naked_return {
411 "".to_string()
412 } else {
413 format!(" -> {return_wrapper_type}")
414 };
415 TPL.interpolate(&HashMap::from([
416 ("attributes", attributes),
417 ("debug", self.get_debug()),
418 ("identifier", identifier),
419 ("args", args.join(", ")),
420 ("return_type", return_wrapper_type),
421 ]))
422 }
423
424 fn gen_cpp(&self) -> String {
425 static TPL: Lazy<CompiledInterpolation> = Lazy::new(|| include_str!("tpl/func/cpp.tpl.cpp").compile_interpolation());
426
427 if matches!(self.cpp_body(), FuncCppBody::Absent) {
428 return "".to_string();
429 }
430
431 let identifier = self.identifier();
432
433 let kind = self.kind();
434 let return_kind = self.return_kind();
435 let return_type_ref = self.return_type_ref();
436
437 let mut attributes_begin = String::new();
439 let mut attributes_end = String::new();
440 if let Some((_, cpp_attr)) = self.cfg_attrs() {
441 attributes_begin = format!("#if {cpp_attr}");
442 attributes_end = "#endif".to_string();
443 }
444
445 let args = cpp_disambiguate_names(self.arguments().into_owned()).collect::<Vec<_>>();
447 let mut decl_args = Vec::with_capacity(args.len());
448 let mut pre_call_args = Vec::with_capacity(args.len());
449 let mut call_args = Vec::with_capacity(args.len());
450 let mut post_call_args = Vec::with_capacity(args.len());
451 if let Some(cls) = kind.as_instance_method() {
452 decl_args.push(
453 cls.type_ref()
454 .with_inherent_constness(self.constness())
455 .render_lane()
456 .to_dyn()
457 .cpp_arg_func_decl("instance")
458 .into_owned(),
459 );
460 }
461 for (name, arg) in &args {
462 let arg_type_ref = arg.type_ref();
463 let render_lane = arg_type_ref.render_lane();
464 let render_lane = render_lane.to_dyn();
465 decl_args.push(render_lane.cpp_arg_func_decl(name).into_owned());
466 pre_post_arg_handle(render_lane.cpp_arg_pre_call(name), &mut pre_call_args);
467 call_args.push(render_lane.cpp_arg_func_call(name));
468 pre_post_arg_handle(render_lane.cpp_arg_post_call(name), &mut post_call_args);
469 }
470
471 let ocv_ret_name = "ocvrs_return";
473 let cpp_extern_return = return_type_ref.cpp_extern_return();
474 let ret_full = if return_kind.is_infallible() {
475 cpp_extern_return
476 } else {
477 return_type_ref.cpp_extern_return_fallible()
478 };
479 let return_type_ref_mut = return_type_ref.as_ref().clone().with_inherent_constness(Constness::Mut);
480 let ret_wrapper_full_mut = if return_kind.is_infallible() {
481 return_type_ref_mut.cpp_extern_return()
482 } else {
483 return_type_ref_mut.cpp_extern_return_fallible()
484 };
485 if !return_kind.is_naked() {
486 decl_args.push(format!("{ret_wrapper_full_mut}* {ocv_ret_name}"));
487 }
488 let return_spec = if return_kind.is_naked() {
489 Borrowed(ret_full.as_ref())
490 } else {
491 "void".into()
492 };
493 let (ret, ret_cast) = cpp_return_map(&return_type_ref, "ret", kind.as_constructor().is_some());
494
495 let func_try = if return_kind.is_infallible() {
497 ""
498 } else {
499 "try {"
500 };
501 let catch = if return_kind.is_infallible() {
502 "".to_string()
503 } else {
504 format!("}} OCVRS_CATCH({ocv_ret_name});")
505 };
506
507 TPL.interpolate(&HashMap::from([
508 ("attributes_begin", attributes_begin.as_str()),
509 ("debug", &self.get_debug()),
510 ("return_spec", &return_spec),
511 ("identifier", &identifier),
512 ("decl_args", &decl_args.join(", ")),
513 ("try", func_try),
514 ("pre_call_args", &pre_call_args.join("\n")),
515 ("call", &cpp_call(self, &kind, &call_args, &return_type_ref)),
516 ("post_call_args", &post_call_args.join("\n")),
517 (
518 "return",
519 &cpp_return(self, return_kind, &ret, ret_cast.then(|| ret_full.as_ref()), ocv_ret_name),
520 ),
521 ("catch", &catch),
522 ("attributes_end", &attributes_end),
523 ]))
524 }
525}
526
527fn pre_post_arg_handle(mut arg: String, args: &mut Vec<String>) {
528 if !arg.is_empty() {
529 arg.push(';');
530 args.push(arg);
531 }
532}
533
534fn rust_call(
535 f: &Func,
536 identifier: &str,
537 func_name: &str,
538 call_args: &[String],
539 forward_args: &[&str],
540 return_kind: ReturnKind,
541) -> String {
542 #![allow(clippy::too_many_arguments)]
543 static CALL_TPL: Lazy<CompiledInterpolation> =
544 Lazy::new(|| "{{ret_receive}}unsafe { sys::{{identifier}}({{call_args}}) };".compile_interpolation());
545
546 let ret_receive = if return_kind.is_naked() {
547 "let ret = "
548 } else {
549 ""
550 };
551 let tpl = match f.rust_body() {
552 FuncRustBody::Auto => Borrowed(&*CALL_TPL),
553 FuncRustBody::ManualCall(body) | FuncRustBody::ManualCallReturn(body) => Owned(body.compile_interpolation()),
554 };
555 tpl.interpolate(&HashMap::from([
556 ("ret_receive", ret_receive),
557 ("identifier", identifier),
558 ("name", func_name),
559 ("call_args", &call_args.join(", ")),
560 ("forward_args", &forward_args.join(", ")),
561 ]))
562}
563
564fn rust_return(
565 f: &Func,
566 return_type_ref: &TypeRef,
567 return_kind: ReturnKind,
568 lifetime: Lifetime,
569) -> (&'static str, String, &'static str) {
570 match f.rust_body() {
571 FuncRustBody::Auto | FuncRustBody::ManualCall(_) => {
572 let ret_pre = if !return_kind.is_naked() {
573 "return_send!(via ocvrs_return);"
574 } else {
575 ""
576 };
577
578 let mut ret_convert = Vec::with_capacity(3);
579 if !return_kind.is_naked() {
580 ret_convert.push(Borrowed("return_receive!(ocvrs_return => ret);"));
581 }
582 if !return_kind.is_infallible() {
583 ret_convert.push("let ret = ret.into_result()?;".into())
584 }
585 let ret_map = rust_return_map(return_type_ref, "ret", return_kind, lifetime);
586 if !ret_map.is_empty() {
587 ret_convert.push(format!("let ret = {ret_map};").into());
588 }
589
590 let ret_stmt = if return_kind.is_infallible() {
591 "ret"
592 } else {
593 "Ok(ret)"
594 };
595 (ret_pre, ret_convert.join("\n"), ret_stmt)
596 }
597 FuncRustBody::ManualCallReturn(_) => ("", "".to_string(), ""),
598 }
599}
600
601fn rust_return_map(return_type: &TypeRef, ret_name: &str, return_kind: ReturnKind, lifetime: Lifetime) -> Cow<'static, str> {
602 let return_type_kind = return_type.kind();
603 if return_type_kind.as_string(return_type.type_hint()).is_some() || return_type_kind.extern_pass_kind().is_by_void_ptr() {
604 format!(
605 "unsafe {{ {typ}::opencv_from_extern({ret_name}) }}",
606 typ = return_type.rust_return(FishStyle::Turbo, lifetime),
607 )
608 .into()
609 } else if return_type_kind.as_pointer().is_some_and(|i| !i.kind().is_void())
610 && !return_type_kind.is_rust_by_ptr(return_type.type_hint())
611 || return_type_kind.as_fixed_array().is_some()
612 {
613 let ptr_call = if return_type.constness().is_const() {
614 "as_ref"
615 } else {
616 "as_mut"
617 };
618 let error_handling = if return_kind.is_infallible() {
619 ".expect(\"Function returned null pointer\")"
620 } else {
621 ".ok_or_else(|| Error::new(core::StsNullPtr, \"Function returned null pointer\"))?"
622 };
623 format!("unsafe {{ {ret_name}.{ptr_call}() }}{error_handling}").into()
624 } else {
625 "".into()
626 }
627}
628
629fn cpp_call(f: &Func, kind: &FuncKind, call_args: &[String], return_type_ref: &TypeRef) -> String {
630 static CALL_TPL: Lazy<CompiledInterpolation> = Lazy::new(|| "{{name}}({{args}})".compile_interpolation());
631
632 static VOID_TPL: Lazy<CompiledInterpolation> = Lazy::new(|| "{{call}};".compile_interpolation());
633
634 static RETURN_TPL: Lazy<CompiledInterpolation> =
635 Lazy::new(|| "{{ret_with_type}} = {{doref}}{{call}};".compile_interpolation());
636
637 static CONSTRUCTOR_TPL: Lazy<CompiledInterpolation> = Lazy::new(|| "{{ret_with_type}}({{args}});".compile_interpolation());
638
639 static CONSTRUCTOR_NO_ARGS_TPL: Lazy<CompiledInterpolation> = Lazy::new(|| "{{ret_with_type}};".compile_interpolation());
640
641 static BOXED_CONSTRUCTOR_TPL: Lazy<CompiledInterpolation> =
642 Lazy::new(|| "{{ret_type}}* ret = new {{ret_type}}({{args}});".compile_interpolation());
643
644 let call_args = call_args.join(", ");
645
646 let return_type_kind = return_type_ref.kind();
647 let ret_type = return_type_ref.cpp_name(CppNameStyle::Reference);
648 let ret_with_type = return_type_ref.cpp_name_ext(CppNameStyle::Reference, "ret", true);
649 let doref = if return_type_kind.as_fixed_array().is_some() {
650 "&"
651 } else {
652 ""
653 };
654
655 let call_name = match kind {
656 FuncKind::Constructor(cls) => cls.cpp_name(CppNameStyle::Reference),
657 FuncKind::Function | FuncKind::GenericFunction | FuncKind::StaticMethod(..) | FuncKind::FunctionOperator(..) => {
658 f.cpp_name(CppNameStyle::Reference)
659 }
660 FuncKind::FieldAccessor(cls, fld) => cpp_method_call_name(
661 cls.type_ref().kind().extern_pass_kind().is_by_ptr(),
662 &fld.cpp_name(CppNameStyle::Declaration),
663 )
664 .into(),
665 FuncKind::InstanceMethod(cls)
666 | FuncKind::GenericInstanceMethod(cls)
667 | FuncKind::ConversionMethod(cls)
668 | FuncKind::InstanceOperator(cls, ..) => cpp_method_call_name(
669 cls.type_ref().kind().extern_pass_kind().is_by_ptr(),
670 &f.cpp_name(CppNameStyle::Declaration),
671 )
672 .into(),
673 };
674
675 let mut inter_vars = HashMap::from([
676 ("ret_type", ret_type),
677 ("ret_with_type", ret_with_type),
678 ("doref", doref.into()),
679 ("args", call_args.as_str().into()),
680 ("name", call_name),
681 ]);
682
683 let (call_tpl, full_tpl) = match f.cpp_body() {
684 FuncCppBody::Auto { .. } => {
685 if let Some(cls) = kind.as_constructor() {
686 if cls.kind().is_boxed() {
687 (None, Some(Borrowed(&*BOXED_CONSTRUCTOR_TPL)))
688 } else if call_args.is_empty() {
689 (None, Some(Borrowed(&*CONSTRUCTOR_NO_ARGS_TPL)))
690 } else {
691 (None, Some(Borrowed(&*CONSTRUCTOR_TPL)))
692 }
693 } else {
694 (Some(Borrowed(&*CALL_TPL)), None)
695 }
696 }
697 FuncCppBody::ManualCall(call) => (Some(Owned(call.compile_interpolation())), None),
698 FuncCppBody::ManualCallReturn(full_tpl) => (None, Some(Owned(full_tpl.compile_interpolation()))),
699 FuncCppBody::Absent => (None, None),
700 };
701 let tpl = full_tpl
702 .or_else(|| {
703 call_tpl.map(|call_tpl| {
704 let call = call_tpl.interpolate(&inter_vars);
705 inter_vars.insert("call", call.into());
706 if return_type_ref.kind().is_void() {
707 Borrowed(&*VOID_TPL)
708 } else {
709 Borrowed(&*RETURN_TPL)
710 }
711 })
712 })
713 .expect("Impossible");
714
715 tpl.interpolate(&inter_vars)
716}
717
718fn cpp_return(f: &Func, return_kind: ReturnKind, ret: &str, ret_cast: Option<&str>, ocv_ret_name: &str) -> Cow<'static, str> {
719 match &f.cpp_body() {
720 FuncCppBody::Auto | FuncCppBody::ManualCall(_) => match return_kind {
721 ReturnKind::InfallibleNaked => {
722 if ret.is_empty() {
723 "".into()
724 } else {
725 let cast = if let Some(ret_type) = ret_cast {
726 format!("({ret_type})")
727 } else {
728 "".to_string()
729 };
730 format!("return {cast}{ret};").into()
731 }
732 }
733 ReturnKind::InfallibleViaArg => {
734 if ret.is_empty() {
735 "".into()
736 } else {
737 format!("*{ocv_ret_name} = {ret};").into()
738 }
739 }
740 ReturnKind::Fallible => {
741 if ret.is_empty() {
742 format!("Ok({ocv_ret_name});").into()
743 } else {
744 let cast = if let Some(ret_type) = ret_cast {
745 format!("<{ret_type}>")
746 } else {
747 "".to_string()
748 };
749 format!("Ok{cast}({ret}, {ocv_ret_name});").into()
750 }
751 }
752 },
753 FuncCppBody::ManualCallReturn(_) | FuncCppBody::Absent => "".into(),
754 }
755}
756
757pub fn cpp_return_map<'f>(return_type: &TypeRef, name: &'f str, is_constructor: bool) -> (Cow<'f, str>, bool) {
758 let return_kind = return_type.kind();
759 if return_kind.is_void() {
760 ("".into(), false)
761 } else if let Some((_, string_type)) = return_kind.as_string(return_type.type_hint()) {
762 let str_mk = match string_type {
763 StrType::StdString(StrEnc::Text) | StrType::CvString(StrEnc::Text) => {
764 format!("ocvrs_create_string({name}.c_str())").into()
765 }
766 StrType::StdString(StrEnc::Binary) => format!("ocvrs_create_byte_string({name}.data(), {name}.size())").into(),
767 StrType::CvString(StrEnc::Binary) => format!("ocvrs_create_byte_string({name}.begin(), {name}.size())").into(),
768 StrType::CharPtr(StrEnc::Text) => format!("ocvrs_create_string({name})").into(),
769 StrType::CharPtr(StrEnc::Binary) => panic!("Returning a byte string via char* is not supported yet"),
770 };
771 (str_mk, false)
772 } else if return_kind.extern_pass_kind().is_by_void_ptr() && !is_constructor {
773 let ret_source = return_type.source();
774 let out = ret_source.kind().as_class().filter(|cls| cls.is_abstract()).map_or_else(
775 || {
776 let deref_count = return_type.kind().as_pointer().map_or(0, |_| 1);
778 format!(
779 "new {typ}({:*<deref_count$}{name})",
780 "",
781 typ = ret_source.cpp_name(CppNameStyle::Reference)
782 )
783 .into()
784 },
785 |_| name.into(),
786 );
787 (out, false)
788 } else {
789 (name.into(), return_kind.as_fixed_array().is_some())
790 }
791}
792
793fn cpp_method_call_name(extern_by_ptr: bool, method_name: &str) -> String {
794 if extern_by_ptr {
795 format!("instance->{method_name}")
796 } else {
797 format!("instance.{method_name}")
798 }
799}
800
801pub fn cpp_disambiguate_names<'tu, 'ge>(
802 args: impl IntoIterator<Item = Field<'tu, 'ge>>,
803) -> impl Iterator<Item = (String, Field<'tu, 'ge>)>
804where
805 'tu: 'ge,
806{
807 let args = args.into_iter();
808 let size_hint = args.size_hint();
809 NamePool::with_capacity(size_hint.1.unwrap_or(size_hint.0)).into_disambiguator(args, |f| f.cpp_name(CppNameStyle::Declaration))
810}
811
812fn rust_generic_decl<'f>(f: &'f Func, return_type_ref: &TypeRef) -> Cow<'f, str> {
813 let mut decls = vec![];
814 if let Some((_, _, lt)) = return_type_ref.type_hint().as_boxed_as_ref() {
815 decls.push(lt.to_string());
816 }
817 match f {
818 Func::Clang { .. } => {}
819 Func::Desc(desc) => {
820 decls.reserve(desc.rust_generic_decls.len());
821 for (typ, constraint) in desc.rust_generic_decls.as_ref() {
822 decls.push(format!("{typ}: {constraint}"));
823 }
824 }
825 }
826 let decls = decls.join(", ");
827 if decls.is_empty() {
828 "".into()
829 } else {
830 format!("<{decls}>").into()
831 }
832}
833
834fn viable_default_arg(arg: &Field) -> bool {
835 arg.default_value().is_some() && !arg.is_user_data() && {
836 let type_ref = arg.type_ref();
837 !matches!(
839 type_ref.type_hint(),
840 TypeRefTypeHint::Slice | TypeRefTypeHint::LenForSlice(..)
841 )
842 }
843}
844
845fn split_default_args<'a, 'tu, 'ge>(args: &'a [Field<'tu, 'ge>]) -> (&'a [Field<'tu, 'ge>], &'a [Field<'tu, 'ge>]) {
849 let last_non_default_arg_idx = args.iter().rposition(|arg| !viable_default_arg(arg));
851 if let Some(last_non_default_arg_idx) = last_non_default_arg_idx {
852 args.split_at(last_non_default_arg_idx + 1)
853 } else {
854 (&[], args)
855 }
856}
857
858fn companion_func_default_args<'tu, 'ge>(f: &Func<'tu, 'ge>) -> Option<Func<'tu, 'ge>> {
860 if f.kind().as_field_accessor().is_some() {
861 return None;
862 }
863
864 match f {
865 Func::Clang { gen_env, .. } => {
866 if gen_env
867 .settings
868 .func_companion_tweak
869 .get(&mut f.matcher())
870 .is_some_and(|t| t.skip_default())
871 {
872 return None;
873 }
874 }
875 Func::Desc(_) => {}
876 }
877
878 let args = f.arguments();
879 let (args_without_def, args_with_def) = split_default_args(&args);
880 if args_with_def.is_empty() {
881 return None;
882 }
883 let original_rust_leafname = f.rust_leafname(FishStyle::No);
884 let mut doc_comment = f.doc_comment().into_owned();
885 let rust_leafname = format!("{}_def", original_rust_leafname);
886 let default_args = comment::render_cpp_default_args(args_with_def);
887 if !doc_comment.is_empty() {
888 doc_comment.push_str("\n\n");
889 }
890 write!(
891 &mut doc_comment,
892 "## Note\nThis alternative version of [{refr}] function uses the following default values for its arguments:\n{default_args}",
893 refr = render_ref(f, Some(&original_rust_leafname))
894 )
895 .expect("Impossible");
896 let mut desc = f.to_desc_with_skip_config(InheritConfig::empty().doc_comment().arguments());
897 let desc_mut = Rc::make_mut(&mut desc);
898 desc_mut.rust_custom_leafname = Some(rust_leafname.into());
899 desc_mut.arguments = args_without_def.into();
900 desc_mut.doc_comment = doc_comment.into();
901 let out = Func::Desc(desc);
902 if out.exclude_kind().is_included() {
903 Some(out)
904 } else {
905 None
906 }
907}
908
909fn companion_func_boxref_mut<'tu, 'ge>(f: &Func<'tu, 'ge>) -> Option<Func<'tu, 'ge>> {
911 let ret_type_ref = f.return_type_ref();
912 if let Some((Constness::Mut, borrow_arg_names, _)) = ret_type_ref.type_hint().as_boxed_as_ref() {
913 let mut desc = f.to_desc_with_skip_config(InheritConfig::empty());
914 let desc_mut = Rc::make_mut(&mut desc);
915 let mut cloned_args = None;
916 let args = if let Some(args) = Rc::get_mut(&mut desc_mut.arguments) {
918 args
919 } else {
920 cloned_args = Some(desc_mut.arguments.to_vec());
921 cloned_args.as_mut().unwrap()
922 };
923 let mut borrow_arg_is_const = false;
924 if borrow_arg_names.contains(&ARG_OVERRIDE_SELF) {
925 if desc_mut.kind.as_instance_method().is_some() && desc_mut.constness.is_const() {
926 borrow_arg_is_const = true;
927 desc_mut.constness = Constness::Mut;
928 }
929 } else {
930 let borrow_arg = args
931 .iter_mut()
932 .find(|arg| borrow_arg_names.contains(&arg.cpp_name(CppNameStyle::Declaration).as_ref()));
933 if let Some(borrow_arg) = borrow_arg {
934 let type_ref = borrow_arg.type_ref();
935 let kind = type_ref.kind();
936 borrow_arg_is_const = type_ref.constness().is_const()
937 && kind
938 .as_pointer_reference_move()
939 .is_some_and(|ptr_or_ref| ptr_or_ref.kind().as_class().is_some());
940 if borrow_arg_is_const {
941 *borrow_arg = borrow_arg.clone().with_type_ref(
942 borrow_arg
943 .type_ref()
944 .map_ptr_ref(|inner| inner.clone().with_inherent_constness(Constness::Mut)),
945 );
946 }
947 }
948 }
949 if borrow_arg_is_const {
950 if let Some(args) = cloned_args {
951 desc_mut.arguments = args.into();
952 }
953 desc_mut.rust_custom_leafname = Some(format!("{}_mut", f.rust_leafname(FishStyle::No)).into());
954 desc_mut.return_type_ref.set_inherent_constness(Constness::Mut);
955 Some(Func::Desc(desc))
956 } else {
957 None
958 }
959 } else {
960 None
961 }
962}