Skip to main content

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