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