use std::borrow::Cow;
use std::collections::HashMap;
use std::sync::LazyLock;
use super::class::ClassExt;
use super::element::RustElement;
use super::type_ref::TypeRefExt;
use super::RustNativeGeneratedElement;
use crate::class::ClassDesc;
use crate::field::{Field, FieldDesc};
use crate::func::{FuncCppBody, FuncDesc, FuncKind, ReturnKind};
use crate::smart_ptr::SmartPtrDesc;
use crate::type_ref::{Constness, CppNameStyle, FishStyle, NameStyle, TypeRef, TypeRefKind};
use crate::writer::rust_native::class::rust_generate_debug_fields;
use crate::{Class, CompiledInterpolation, Element, Func, IteratorExt, SmartPtr, StrExt, StringExt, SupportedModule};
impl RustElement for SmartPtr<'_, '_> {
fn rust_module(&self) -> SupportedModule {
self.pointee().rust_module()
}
fn rust_module_reference(&self) -> Cow<'_, str> {
"core".into()
}
fn rust_name(&self, style: NameStyle) -> Cow<'_, str> {
let decl_name = self.rust_leafname(style.turbo_fish_style());
match style {
NameStyle::Declaration => decl_name,
NameStyle::Reference(_) => {
let mut out = self.rust_module_reference().into_owned();
out.extend_sep("::", &decl_name);
out.into()
}
}
}
fn rust_leafname(&self, fish_style: FishStyle) -> Cow<'_, str> {
format!(
"Ptr{fish}<{typ}>",
fish = fish_style.rust_qual(),
typ = self.pointee().rust_name(NameStyle::ref_()),
)
.into()
}
}
impl RustNativeGeneratedElement for SmartPtr<'_, '_> {
fn element_safe_id(&self) -> String {
format!("{}-{}", self.rust_module().opencv_name(), self.rust_localalias())
}
fn gen_rust(&self, _opencv_version: &str) -> String {
static TPL: LazyLock<CompiledInterpolation> =
LazyLock::new(|| include_str!("tpl/smart_ptr/rust.tpl.rs").compile_interpolation());
static TRAIT_RAW_TPL: LazyLock<CompiledInterpolation> =
LazyLock::new(|| include_str!("tpl/smart_ptr/trait_raw.tpl.rs").compile_interpolation());
static BASE_CAST_TPL: LazyLock<CompiledInterpolation> =
LazyLock::new(|| include_str!("tpl/smart_ptr/base_cast.tpl.rs").compile_interpolation());
static IMPL_DEBUG_TPL: LazyLock<CompiledInterpolation> =
LazyLock::new(|| include_str!("tpl/smart_ptr/impl_debug.rs").compile_interpolation());
static CTOR_TPL: LazyLock<CompiledInterpolation> =
LazyLock::new(|| include_str!("tpl/smart_ptr/ctor.tpl.rs").compile_interpolation());
let rust_localalias = self.rust_localalias();
let rust_full = self.rust_name(NameStyle::ref_());
let pointee_type = self.pointee();
let pointee_kind = pointee_type.kind();
let inner_rust_full = pointee_type.rust_name(NameStyle::ref_());
let type_ref = self.type_ref();
let smartptr_class = smartptr_class(&type_ref);
let extern_get_inner_ptr = method_get_inner_ptr(
smartptr_class.clone(),
pointee_type.as_ref().clone().with_inherent_constness(Constness::Const),
)
.identifier();
let extern_get_inner_ptr_mut = method_get_inner_ptr(
smartptr_class.clone(),
pointee_type.as_ref().clone().with_inherent_constness(Constness::Mut),
)
.identifier();
let mut impls = String::with_capacity(1024);
if let Some(cls) = pointee_kind.as_class().filter(|cls| cls.kind().is_trait()) {
TRAIT_RAW_TPL.interpolate_into(
&mut impls,
&HashMap::from([
("rust_full", rust_full.as_ref()),
("base_rust_as_raw_const", &cls.rust_as_raw_name(Constness::Const)),
("base_rust_as_raw_mut", &cls.rust_as_raw_name(Constness::Mut)),
("base_rust_full_mut", &cls.rust_trait_name(NameStyle::ref_(), Constness::Mut)),
(
"base_rust_full_const",
&cls.rust_trait_name(NameStyle::ref_(), Constness::Const),
),
]),
);
let mut debug_fields = rust_generate_debug_fields(
cls.field_methods(&cls.fields(|f| f.exclude_kind().is_included()), Some(Constness::Const)),
);
for base in all_bases(&cls) {
let base_rust_local = base.rust_name(NameStyle::decl());
TRAIT_RAW_TPL.interpolate_into(
&mut impls,
&HashMap::from([
("rust_full", rust_full.as_ref()),
("base_rust_as_raw_const", &base.rust_as_raw_name(Constness::Const)),
("base_rust_as_raw_mut", &base.rust_as_raw_name(Constness::Mut)),
("base_rust_full_mut", &base.rust_trait_name(NameStyle::ref_(), Constness::Mut)),
(
"base_rust_full_const",
&base.rust_trait_name(NameStyle::ref_(), Constness::Const),
),
]),
);
let extern_cast_to_base = method_cast_to_base(smartptr_class.clone(), base.type_ref(), &base_rust_local).identifier();
BASE_CAST_TPL.interpolate_into(
&mut impls,
&HashMap::from([
("rust_full", rust_full.as_ref()),
("base_rust_full_ref", &base.rust_name(NameStyle::ref_())),
("extern_cast_to_base", &extern_cast_to_base),
]),
);
let base_fields = base.fields(|f| f.exclude_kind().is_included());
let base_field_const_methods = base.field_methods(&base_fields, Some(Constness::Const));
debug_fields.push_str(&rust_generate_debug_fields(base_field_const_methods));
}
IMPL_DEBUG_TPL.interpolate_into(
&mut impls,
&HashMap::from([
("rust_full", rust_full.as_ref()),
("rust_localalias", &rust_localalias),
("debug_fields", &debug_fields),
]),
);
};
let rust_as_raw_const = type_ref.rust_as_raw_name(Constness::Const);
let rust_as_raw_mut = type_ref.rust_as_raw_name(Constness::Mut);
let ctor = if gen_ctor(&pointee_kind) {
let extern_new = method_new(smartptr_class.clone(), type_ref.clone(), pointee_type.as_ref().clone()).identifier();
CTOR_TPL.interpolate(&HashMap::from([
("inner_rust_full", inner_rust_full.as_ref()),
("extern_new", &extern_new),
]))
} else {
"".to_string()
};
let extern_new_null = method_new_null(smartptr_class.clone(), type_ref).identifier();
let extern_delete = FuncDesc::method_delete(smartptr_class).identifier();
TPL.interpolate(&HashMap::from([
("rust_localalias", rust_localalias.as_ref()),
("rust_as_raw_const", &rust_as_raw_const),
("rust_as_raw_mut", &rust_as_raw_mut),
("rust_full", &rust_full),
("inner_rust_full", &inner_rust_full),
("extern_new_null", &extern_new_null),
("extern_delete", &extern_delete),
("extern_get_inner_ptr", &extern_get_inner_ptr),
("extern_get_inner_ptr_mut", &extern_get_inner_ptr_mut),
("ctor", &ctor),
("impls", &impls),
]))
}
fn gen_rust_externs(&self) -> String {
extern_functions(self).iter().map(Func::gen_rust_externs).join("")
}
fn gen_cpp(&self) -> String {
static TPL: LazyLock<CompiledInterpolation> =
LazyLock::new(|| include_str!("tpl/smart_ptr/cpp.tpl.cpp").compile_interpolation());
TPL.interpolate(&[("methods", extern_functions(self).iter().map(Func::gen_cpp).join(""))].into())
}
}
fn extern_functions<'tu, 'ge>(ptr: &SmartPtr<'tu, 'ge>) -> Vec<Func<'tu, 'ge>> {
let type_ref = ptr.type_ref();
let pointee_type = ptr.pointee();
let pointee_kind = pointee_type.kind();
let smartptr_class = smartptr_class(&type_ref);
let mut out = Vec::with_capacity(6);
out.push(method_get_inner_ptr(
smartptr_class.clone(),
pointee_type.as_ref().clone().with_inherent_constness(Constness::Const),
));
out.push(method_get_inner_ptr(
smartptr_class.clone(),
pointee_type.as_ref().clone().with_inherent_constness(Constness::Mut),
));
out.push(method_new_null(smartptr_class.clone(), type_ref.clone()));
out.push(FuncDesc::method_delete(smartptr_class.clone()));
if let Some(cls) = pointee_kind.as_class().filter(|cls| cls.kind().is_trait()) {
for base in all_bases(&cls) {
out.push(method_cast_to_base(
smartptr_class.clone(),
base.type_ref(),
&base.rust_name(NameStyle::decl()),
));
}
}
if gen_ctor(&pointee_kind) {
out.push(method_new(smartptr_class, type_ref, pointee_type.into_owned()));
}
out
}
fn gen_ctor(pointee_kind: &TypeRefKind) -> bool {
match pointee_kind.canonical().as_ref() {
TypeRefKind::Primitive(_, _) => true,
TypeRefKind::Class(cls) => !cls.is_abstract(),
_ => false,
}
}
fn all_bases<'tu, 'ge>(cls: &Class<'tu, 'ge>) -> Vec<Class<'tu, 'ge>> {
let mut out = cls
.all_bases()
.into_iter()
.filter(|b| b.exclude_kind().is_included())
.collect::<Vec<_>>();
out.sort_unstable_by(|left, right| {
left
.cpp_name(CppNameStyle::Reference)
.cmp(&right.cpp_name(CppNameStyle::Reference))
});
out
}
pub trait SmartPtrExt {
fn rust_localalias(&self) -> Cow<'_, str>;
}
impl SmartPtrExt for SmartPtr<'_, '_> {
fn rust_localalias(&self) -> Cow<'_, str> {
format!("PtrOf{typ}", typ = self.pointee().rust_safe_id(false)).into()
}
}
fn smartptr_class<'tu, 'ge>(smart_ptr_type_ref: &TypeRef<'tu, 'ge>) -> Class<'tu, 'ge> {
Class::new_desc(ClassDesc::boxed(
smart_ptr_type_ref.cpp_name(CppNameStyle::Reference),
SupportedModule::Core,
))
}
fn method_new<'tu, 'ge>(
smartptr_class: Class<'tu, 'ge>,
smartptr_type_ref: TypeRef<'tu, 'ge>,
pointee_type: TypeRef<'tu, 'ge>,
) -> Func<'tu, 'ge> {
let pointee_kind = pointee_type.kind();
let val = if pointee_kind.is_copy(pointee_type.type_hint()) {
if pointee_kind.as_class().is_some_and(|cls| cls.kind().is_simple()) {
panic!("Ptr with simple class is not supported");
} else {
format!("new {typ}(val)", typ = pointee_type.cpp_name(CppNameStyle::Reference)).into()
}
} else {
Cow::Borrowed("val")
};
Func::new_desc(
FuncDesc::new(
FuncKind::Constructor(smartptr_class),
Constness::Const,
ReturnKind::InfallibleNaked,
"new",
SupportedModule::Core,
[Field::new_desc(FieldDesc::new("val", pointee_type))],
smartptr_type_ref,
)
.cpp_body(FuncCppBody::ManualCallReturn(
format!("return new {{{{ret_type}}}}({val});").into(),
)),
)
}
fn method_new_null<'tu, 'ge>(smartptr_class: Class<'tu, 'ge>, smartptr_type_ref: TypeRef<'tu, 'ge>) -> Func<'tu, 'ge> {
Func::new_desc(
FuncDesc::new(
FuncKind::Constructor(smartptr_class),
Constness::Const,
ReturnKind::InfallibleNaked,
"new_null",
SupportedModule::Core,
[],
smartptr_type_ref,
)
.cpp_body(FuncCppBody::ManualCallReturn("return new {{ret_type}}();".into())),
)
}
fn method_cast_to_base<'tu, 'ge>(
smartptr_class: Class<'tu, 'ge>,
base_type_ref: TypeRef<'tu, 'ge>,
base_rust_local: &str,
) -> Func<'tu, 'ge> {
let cpp_body = FuncCppBody::ManualCallReturn(
format!(
"return new {{{{ret_type}}}}(instance->dynamicCast<{base_type}>());",
base_type = base_type_ref.cpp_name(CppNameStyle::Reference)
)
.into(),
);
Func::new_desc(
FuncDesc::new(
FuncKind::InstanceMethod(smartptr_class),
Constness::Mut,
ReturnKind::InfallibleNaked,
format!("to_PtrOf{base_rust_local}"),
SupportedModule::Core,
[],
TypeRef::new_smartptr(SmartPtr::new_desc(SmartPtrDesc::new(base_type_ref))),
)
.cpp_body(cpp_body),
)
}
fn method_get_inner_ptr<'tu, 'ge>(smartptr_class: Class<'tu, 'ge>, pointee_type: TypeRef<'tu, 'ge>) -> Func<'tu, 'ge> {
let constness = pointee_type.constness();
let return_type_ref = if pointee_type.kind().extern_pass_kind().is_by_ptr() {
pointee_type
} else {
TypeRef::new_pointer(pointee_type)
};
Func::new_desc(
FuncDesc::new(
FuncKind::InstanceMethod(smartptr_class),
constness,
ReturnKind::InfallibleNaked,
format!("getInnerPtr{}", constness.rust_name_qual()),
SupportedModule::Core,
[],
return_type_ref,
)
.cpp_body(FuncCppBody::ManualCallReturn("return instance->get();".into())),
)
}