opencv_binding_generator/writer/rust_native/
func.rs

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		// attributes
428		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		// arguments
436		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		// return
462		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		// exception handling
486		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				// todo implement higher count if it's needed
780				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		// don't remove the arguments that are used to pass the slice or its length
841		!matches!(
842			type_ref.type_hint(),
843			TypeRefTypeHint::Slice | TypeRefTypeHint::LenForSlice(..)
844		)
845	}
846}
847
848/// Split `arg` into 2 sub-slices, (args_without_default_value, args_with_default_value)
849///
850/// Arguments with special meanings like userdata and slice length are not included in the args_with_default_value slice.
851fn split_default_args<'a, 'tu, 'ge>(args: &'a [Field<'tu, 'ge>]) -> (&'a [Field<'tu, 'ge>], &'a [Field<'tu, 'ge>]) {
852	// default args are in the end
853	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
861/// Companion function with all optional arguments as defaults
862fn 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
912/// Companion function returning `BoxRefMut` for a corresponding function returning `BoxRef`
913fn 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		// Rc::make_mut doesn't work on slices
920		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}