opencv_binding_generator/writer/rust_native/
func.rs

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