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