opencv-binding-generator 0.101.0

Binding generator for opencv crate
Documentation
use std::borrow::Cow;
use std::collections::HashMap;
use std::sync::LazyLock;

use super::element::{DefaultRustNativeElement, RustElement};
use super::type_ref::TypeRefExt;
use super::{disambiguate_single_name, RustNativeGeneratedElement};
use crate::class::ClassDesc;
use crate::field::{Field, FieldDesc};
use crate::func::{FuncCppBody, FuncDesc, FuncKind, ReturnKind};
use crate::type_ref::{Constness, FishStyle};
use crate::{
	Class, CompiledInterpolation, CppNameStyle, EntityElement, Func, IteratorExt, NameStyle, StrExt, SupportedModule, Tuple,
	TypeRef,
};

impl RustElement for Tuple<'_, '_> {
	fn rust_module(&self) -> SupportedModule {
		DefaultRustNativeElement::rust_module(self.entity())
	}

	fn rust_name(&self, style: NameStyle) -> Cow<'_, str> {
		DefaultRustNativeElement::rust_name(self, self.entity(), style).into()
	}

	fn rust_leafname(&self, fish_style: FishStyle) -> Cow<'_, str> {
		format!(
			"Tuple{fish}<{inner}>",
			fish = fish_style.rust_qual(),
			inner = self.rust_inner()
		)
		.into()
	}
}

impl RustNativeGeneratedElement for Tuple<'_, '_> {
	fn element_safe_id(&self) -> String {
		format!("{}-{}", self.rust_element_module().opencv_name(), self.rust_localalias())
	}

	fn gen_rust(&self, _opencv_version: &str) -> String {
		static RUST_TPL: LazyLock<CompiledInterpolation> =
			LazyLock::new(|| include_str!("tpl/tuple/rust.tpl.rs").compile_interpolation());

		let type_ref = self.type_ref();
		let rust_localalias = self.rust_localalias();
		let elements = self.elements();
		let tuple_desc = tuple_class(&type_ref);

		let mut rets = disambiguate_single_name("arg");

		let getters = elements
			.iter()
			.enumerate()
			.map(|(num, typ)| {
				let ret_name = rets.next().expect("Endless iterator");
				let get_extern = method_get(tuple_desc.clone(), typ.clone(), num).identifier();
				format!(
					"{num} = {ret_name}: {typ}, get_{num} via {get_extern}",
					typ = typ.rust_name(NameStyle::ref_()),
				)
			})
			.join(",\n");
		let new_extern = method_new(type_ref.clone(), &elements).identifier();
		let delete_extern = FuncDesc::method_delete(tuple_desc).identifier();

		RUST_TPL.interpolate(&HashMap::from([
			("rust_localalias", rust_localalias),
			("rust_as_raw_const", type_ref.rust_as_raw_name(Constness::Const).into()),
			("rust_as_raw_mut", type_ref.rust_as_raw_name(Constness::Mut).into()),
			("rust_full", self.rust_name(NameStyle::ref_())),
			("inner_rust_full", self.rust_inner().into()),
			("getters", getters.into()),
			("new_extern", new_extern.into()),
			("delete_extern", delete_extern.into()),
		]))
	}

	fn gen_rust_externs(&self) -> String {
		extern_functions(self).iter().map(Func::gen_rust_externs).join("")
	}

	fn gen_cpp(&self) -> String {
		static CPP_TPL: LazyLock<CompiledInterpolation> =
			LazyLock::new(|| include_str!("tpl/tuple/cpp.tpl.cpp").compile_interpolation());

		CPP_TPL.interpolate(&HashMap::from([(
			"methods",
			extern_functions(self).iter().map(Func::gen_cpp).join(""),
		)]))
	}
}

fn extern_functions<'tu, 'ge>(tuple: &Tuple<'tu, 'ge>) -> Vec<Func<'tu, 'ge>> {
	let type_ref = tuple.type_ref();
	let elements = tuple.elements();
	let tuple_desc = tuple_class(&type_ref);

	let mut out = Vec::with_capacity(elements.len() + 2);
	out.push(method_new(type_ref, &elements));
	for (i, typ) in elements.into_iter().enumerate() {
		out.push(method_get(tuple_desc.clone(), typ, i));
	}
	out.push(FuncDesc::method_delete(tuple_desc));
	out
}

pub trait TupleExt {
	fn rust_localalias(&self) -> Cow<'_, str>;
	fn rust_inner(&self) -> String;
	fn rust_element_module(&self) -> SupportedModule;
}

impl TupleExt for Tuple<'_, '_> {
	fn rust_localalias(&self) -> Cow<'_, str> {
		const PREFIX: &str = "TupleOf";

		let elems = self.elements();
		let mut out = String::with_capacity(PREFIX.len() + elems.len() * 16);
		out.push_str(PREFIX);
		for (i, elem) in elems.into_iter().enumerate() {
			if i != 0 {
				out.push('_');
			}
			out.push_str(&elem.rust_safe_id(true))
		}
		out.into()
	}

	fn rust_inner(&self) -> String {
		let elems = self.elements();
		let mut out = String::with_capacity(2 + elems.len() * 16);
		out.push('(');
		for (i, elem) in elems.into_iter().enumerate() {
			if i != 0 {
				out.push_str(", ");
			}
			out.push_str(&elem.rust_name(NameStyle::ref_()));
		}
		out.push(')');
		out
	}

	fn rust_element_module(&self) -> SupportedModule {
		self
			.elements()
			.iter()
			.map(|elem_type| elem_type.rust_module())
			.filter(|m| !matches!(m, SupportedModule::Core))
			.reduce(|single_module, elem_module| {
				if single_module != elem_module {
					panic!("Too many element modules: {single_module:?} and at least {elem_module:?}")
				} else {
					single_module
				}
			})
			.unwrap_or_else(|| self.rust_module())
	}
}

fn tuple_class<'tu, 'ge>(typle_type_ref: &TypeRef<'tu, 'ge>) -> Class<'tu, 'ge> {
	Class::new_desc(ClassDesc::boxed(
		typle_type_ref.cpp_name(CppNameStyle::Reference),
		SupportedModule::Core,
	))
}

fn method_new<'tu, 'ge>(tuple_typeref: TypeRef<'tu, 'ge>, elements: &[TypeRef<'tu, 'ge>]) -> Func<'tu, 'ge> {
	let arguments = disambiguate_single_name("arg")
		.zip(elements.iter())
		.map(|(arg_name, type_ref)| Field::new_desc(FieldDesc::new(arg_name, type_ref.clone())))
		.collect::<Vec<_>>();
	Func::new_desc(FuncDesc::new(
		FuncKind::Constructor(tuple_class(&tuple_typeref)),
		Constness::Const,
		ReturnKind::InfallibleNaked,
		"new",
		SupportedModule::Core,
		arguments,
		tuple_typeref,
	))
}

fn method_get<'tu, 'ge>(tuple_class: Class<'tu, 'ge>, element_type: TypeRef<'tu, 'ge>, num: usize) -> Func<'tu, 'ge> {
	Func::new_desc(
		FuncDesc::new(
			FuncKind::InstanceMethod(tuple_class),
			Constness::Const,
			ReturnKind::InfallibleViaArg,
			format!("get_{num}"),
			SupportedModule::Core,
			[],
			element_type.clone(),
		)
		.cpp_body(FuncCppBody::ManualCall(format!("std::get<{num}>(*instance)").into())),
	)
}