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