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