use std::{
borrow::Cow,
collections::HashMap,
fmt,
};
use clang::{Availability, Entity, EntityKind};
use maplit::hashmap;
use once_cell::sync::Lazy;
use regex::Regex;
use crate::{
Class,
comment,
CompiledInterpolation,
DefaultElement,
DefinitionLocation,
DependentTypeMode,
Element,
EntityElement,
EntityExt,
Field,
FieldTypeHint,
GeneratedElement,
GeneratorEnv,
get_debug,
IteratorExt,
NamePool,
reserved_rename,
settings::{self, SliceHint},
StrExt,
StringExt,
type_ref::Kind as TypeRefKind,
TypeRef,
};
#[derive(Debug, PartialEq)]
pub enum OperatorKind {
Unsupported,
Index,
}
impl From<&str> for OperatorKind {
fn from(s: &str) -> Self {
match s.trim() {
"[]" => {
OperatorKind::Index
}
_ => {
OperatorKind::Unsupported
},
}
}
}
#[derive(Debug)]
enum Kind<'tu, 'g> {
Function,
FunctionOperator(OperatorKind),
Constructor(Class<'tu, 'g>),
InstanceMethod(Class<'tu, 'g>),
StaticMethod(Class<'tu, 'g>),
FieldAccessor(Class<'tu, 'g>),
ConversionMethod(Class<'tu, 'g>),
InstanceOperator(Class<'tu, 'g>, OperatorKind),
GenericFunction,
GenericInstanceMethod(Class<'tu, 'g>),
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum FunctionTypeHint {
None,
FieldSetter,
Specialized(&'static HashMap<&'static str, &'static str>),
}
#[derive(Clone)]
pub struct Func<'tu, 'g> {
entity: Entity<'tu>,
type_hint: FunctionTypeHint,
name_hint: Option<&'g str>,
gen_env: &'g GeneratorEnv<'tu>,
}
impl<'tu, 'g> Func<'tu, 'g> {
pub fn new(entity: Entity<'tu>, gen_env: &'g GeneratorEnv<'tu>) -> Self {
Self { entity, type_hint: FunctionTypeHint::None, name_hint: None, gen_env }
}
pub fn new_ext(entity: Entity<'tu>, type_hint: FunctionTypeHint, name_hint: Option<&'g str>, gen_env: &'g GeneratorEnv<'tu>) -> Self {
Self { entity, type_hint, name_hint, gen_env }
}
pub fn rust_generate_funcs(fns: impl IntoIterator<Item=&'g Func<'tu, 'g>>, opencv_version: &str) -> String where 'tu: 'g {
let fns = fns.into_iter()
.filter(|f| !f.is_excluded());
Self::rust_disambiguate_names(fns)
.map(|(name, func)| func.gen_rust_with_name(&name, opencv_version))
.join("")
}
pub fn rust_disambiguate_names(fns: impl IntoIterator<Item=&'g Func<'tu, 'g>>) -> impl Iterator<Item=(String, &'g Func<'tu, 'g>)> where 'tu: 'g {
let args = fns.into_iter();
NamePool::with_capacity(args.size_hint().1.unwrap_or_default())
.into_disambiguator(args, |f| f.rust_leafname())
}
fn kind(&self) -> Kind<'tu, 'g> {
const OPERATOR: &str = "operator";
match self.entity.get_kind() {
EntityKind::FunctionDecl => {
if let Some(operator) = self.entity.cpp_localname().strip_str_prefix(OPERATOR) {
Kind::FunctionOperator(operator.into())
} else {
Kind::Function
}
}
EntityKind::Constructor => {
Kind::Constructor(Class::new(
self.entity.get_semantic_parent().expect("Can't get parent of constructor"),
self.gen_env,
))
}
EntityKind::Method => {
let class = Class::new(
self.entity.get_semantic_parent().expect("Can't get parent of method"),
self.gen_env,
);
if self.entity.is_static_method() {
Kind::StaticMethod(class)
} else {
if let Some(operator) = self.entity.cpp_localname().strip_str_prefix(OPERATOR) {
Kind::InstanceOperator(class, operator.into())
} else {
Kind::InstanceMethod(class)
}
}
}
EntityKind::FieldDecl | EntityKind::VarDecl => {
Kind::FieldAccessor(Field::new(self.entity, self.gen_env).parent())
}
EntityKind::ConversionFunction => {
Kind::ConversionMethod(Class::new(
self.entity.get_semantic_parent().expect("Can't get parent of method"),
self.gen_env,
))
}
EntityKind::FunctionTemplate => {
match self.entity.get_template_kind() {
Some(EntityKind::Method) => {
Kind::GenericInstanceMethod(Class::new(
self.entity.get_semantic_parent().expect("Can't get parent of generic method"),
self.gen_env,
))
}
_ => {
Kind::GenericFunction
}
}
}
_ => unreachable!("Unknown function entity: {:#?}", self.entity)
}
}
pub fn as_constructor(&self) -> Option<Class<'tu, 'g>> {
if let Kind::Constructor(out) = self.kind() {
Some(out)
} else {
None
}
}
pub fn as_instance_method(&self) -> Option<Class<'tu, 'g>> {
match self.kind() {
Kind::InstanceMethod(out) | Kind::FieldAccessor(out) | Kind::GenericInstanceMethod(out)
| Kind::ConversionMethod(out) | Kind::InstanceOperator(out, ..) => {
Some(out)
}
_ => {
None
}
}
}
pub fn as_static_method(&self) -> Option<Class<'tu, 'g>> {
if let Kind::StaticMethod(out) = self.kind() {
Some(out)
} else {
None
}
}
pub fn as_field_accessor(&self) -> Option<Field<'tu, 'g>> {
if let Kind::FieldAccessor(..) = self.kind() {
Some(Field::new(self.entity, self.gen_env))
} else {
None
}
}
pub fn as_field_setter(&self) -> Option<Field<'tu, 'g>> {
if self.as_field_accessor().is_some() && self.type_hint == FunctionTypeHint::FieldSetter {
Some(Field::new_ext(self.entity, FieldTypeHint::FieldSetter, self.gen_env))
} else {
None
}
}
pub fn as_conversion_method(&self) -> Option<Class<'tu, 'g>> {
if let Kind::ConversionMethod(out) = self.kind() {
Some(out)
} else {
None
}
}
pub fn as_operator(&self) -> Option<(Option<Class<'tu, 'g>>, OperatorKind)> {
match self.kind() {
Kind::FunctionOperator(kind) => {
Some((None, kind))
}
Kind::InstanceOperator(cls, kind) => {
Some((Some(cls), kind))
}
_ => {
None
}
}
}
pub fn is_const(&self) -> bool {
if settings::FORCE_CONSTANT_METHOD.contains(self.cpp_fullname().as_ref()) {
true
}
else if let Some(fld) = self.as_field_accessor() {
self.type_hint != FunctionTypeHint::FieldSetter && {
let type_ref = fld.type_ref();
type_ref.is_const() || type_ref.is_copy() || type_ref.is_cv_string() || type_ref.is_std_string()
}
} else {
self.entity.is_const_method()
}
}
pub fn is_abstract(&self) -> bool {
self.entity.is_pure_virtual_method()
}
pub fn is_generic(&self) -> bool {
match self.kind() {
Kind::GenericFunction | Kind::GenericInstanceMethod(..) => {
!self.as_specialized().is_some()
}
Kind::Function | Kind::Constructor(..) | Kind::InstanceMethod(..)
| Kind::StaticMethod(..) | Kind::FieldAccessor(..) | Kind::ConversionMethod(..)
| Kind::FunctionOperator(..) | Kind::InstanceOperator(..) => {
false
}
}
}
fn is_infallible(&self) -> bool {
self.as_field_accessor().is_some()
|| self.gen_env.get_export_config(self.entity).map_or(false, |e| e.no_except)
}
pub fn as_specialized(&self) -> Option<&'static HashMap<&'static str, &'static str>> {
if let FunctionTypeHint::Specialized(spec) = self.type_hint {
Some(spec)
} else {
None
}
}
pub fn return_type(&self) -> TypeRef<'tu, 'g> {
match self.kind() {
Kind::Constructor(cls) => {
cls.type_ref()
}
Kind::Function | Kind::InstanceMethod(..) | Kind::StaticMethod(..)
| Kind::ConversionMethod(..) | Kind::GenericInstanceMethod(..) | Kind::GenericFunction
| Kind::FunctionOperator(..) | Kind::InstanceOperator(..) => {
let out = TypeRef::new(
self.entity.get_result_type().expect("Can't get return type"),
self.gen_env,
);
let mut out = if let TypeRefKind::Reference(type_ref) = out.canonical().kind() {
type_ref
} else {
out
};
if let Some(spec) = self.as_specialized() {
if out.is_generic() {
let spec_type = spec.get(out.base().cpp_full().as_ref())
.and_then(|s| self.gen_env.resolve_type(s));
if let Some(spec_type) = spec_type {
out.specialize(spec_type);
}
}
}
out
}
Kind::FieldAccessor(..) => {
if self.type_hint == FunctionTypeHint::FieldSetter {
TypeRef::new(self.gen_env.resolve_type("void").expect("Can't resolve void type"), self.gen_env)
} else {
Field::new(self.entity, self.gen_env).type_ref()
}
}
}
}
pub fn arguments(&self) -> Vec<Field<'tu, 'g>> {
let args = match self.kind() {
Kind::GenericFunction | Kind::GenericInstanceMethod(..) => {
let mut out = vec![];
self.entity.walk_children_while(|child| {
if child.get_kind() == EntityKind::ParmDecl {
out.push(child);
}
true
});
out
}
Kind::FieldAccessor(..) => {
if self.type_hint == FunctionTypeHint::FieldSetter {
vec![self.entity]
} else {
vec![]
}
}
_ => {
self.entity.get_arguments().expect("Can't get arguments")
}
};
let empty_hashmap = HashMap::new();
let spec = if let Some(spec) = self.as_specialized() {
spec
} else {
&empty_hashmap
};
let args_len = args.len();
let func_name = self.cpp_fullname();
let is_field_setter = self.as_field_setter().is_some();
args.into_iter()
.map(|a| {
if is_field_setter {
return Field::new_ext(a, FieldTypeHint::FieldSetter, self.gen_env)
}
if let Some(slice_arg) = settings::SLICE_ARGUMENT.get(&(func_name.as_ref(), args_len)) {
match slice_arg {
&SliceHint::ForceSlice(arg) => {
if arg == a.rust_leafname().as_ref() {
return Field::new_ext(a, FieldTypeHint::Slice, self.gen_env)
}
},
&SliceHint::ConvertSlice(ptr_arg, len_arg, len_div) => {
let arg_name = a.rust_leafname();
if ptr_arg == arg_name.as_ref() {
return Field::new_ext(a, FieldTypeHint::Slice, self.gen_env)
} else if len_arg == arg_name.as_ref() {
return Field::new_ext(a, FieldTypeHint::SliceLen(ptr_arg, len_div), self.gen_env)
}
}
}
}
let out = Field::new(a, self.gen_env);
let type_ref = out.type_ref();
if type_ref.is_generic() {
let spec_type = spec.get(type_ref.base().cpp_full().as_ref())
.and_then(|s| self.gen_env.resolve_type(s));
if let Some(spec_type) = spec_type {
return Field::new_ext(a, FieldTypeHint::Specialized(spec_type), self.gen_env);
}
}
out
})
.collect()
}
pub fn dependent_types(&self) -> Vec<Box<dyn GeneratedElement + 'g>> {
self.arguments().into_iter()
.map(|a| a.type_ref())
.filter(|t| !t.is_ignored())
.map(|t| t.dependent_types())
.flatten()
.chain(self.return_type().dependent_types_with_mode(DependentTypeMode::ForReturn(DefinitionLocation::Module)))
.collect()
}
fn cpp_call_invoke(&self) -> String {
static VOID_TPL: Lazy<CompiledInterpolation> = Lazy::new(||
"{{name}}({{args}});".compile_interpolation()
);
static NORMAL_TPL: Lazy<CompiledInterpolation> = Lazy::new(||
"{{ret_type}} = {{doref}}{{name}}{{generic}}({{args}});".compile_interpolation()
);
static FIELD_READ_TPL: Lazy<CompiledInterpolation> = Lazy::new(||
"{{ret_type}} = {{doref}}{{name}};".compile_interpolation()
);
static FIELD_WRITE_TPL: Lazy<CompiledInterpolation> = Lazy::new(||
"{{name}} = {{args}};".compile_interpolation()
);
static CONSTRUCTOR_TPL: Lazy<CompiledInterpolation> = Lazy::new(||
"{{ret_type}} ret({{args}});".compile_interpolation()
);
static CONSTRUCTOR_NO_ARGS_TPL: Lazy<CompiledInterpolation> = Lazy::new(||
"{{ret_type}} ret;".compile_interpolation()
);
static BOXED_CONSTRUCTOR_TPL: Lazy<CompiledInterpolation> = Lazy::new(||
"{{ret_type}}* ret = new {{ret_type}}({{args}});".compile_interpolation()
);
let call_name = match self.kind() {
Kind::Function | Kind::GenericFunction | Kind::StaticMethod(..)
| Kind::FunctionOperator(..) => {
self.cpp_fullname()
}
Kind::Constructor(class) => {
class.cpp_fullname().into_owned().into()
}
Kind::FieldAccessor(class) if self.type_hint == FunctionTypeHint::FieldSetter => {
class.cpp_method_call_name(DefaultElement::cpp_localname(self).as_ref()).into()
}
Kind::InstanceMethod(class) | Kind::FieldAccessor(class) | Kind::GenericInstanceMethod(class)
| Kind::ConversionMethod(class) | Kind::InstanceOperator(class, ..) => {
class.cpp_method_call_name(self.cpp_localname().as_ref()).into()
}
};
let mut generic = String::new();
if let Some(spec) = self.as_specialized() {
generic.reserve(64);
generic.push('<');
generic.extend_join(spec.values(), ", ");
generic.push('>');
}
let args = Field::cpp_disambiguate_names(self.arguments())
.map(|(name, arg)| arg.type_ref().cpp_arg_func_call(name).into_owned())
.join(", ");
let return_type = self.return_type();
let tpl = if let Some(cls) = self.as_constructor() {
if cls.is_by_ptr() {
&BOXED_CONSTRUCTOR_TPL
} else if args.is_empty() {
&CONSTRUCTOR_NO_ARGS_TPL
} else {
&CONSTRUCTOR_TPL
}
} else if let Kind::FieldAccessor(..) = self.kind() {
if self.type_hint == FunctionTypeHint::FieldSetter {
&FIELD_WRITE_TPL
} else {
&FIELD_READ_TPL
}
} else if return_type.is_void() {
&VOID_TPL
} else {
&NORMAL_TPL
};
let ret_type = if self.as_constructor().is_some() {
return_type.cpp_full()
} else {
return_type.cpp_full_with_name("ret")
};
let doref = if return_type.as_fixed_array().is_some() {
"&"
} else {
""
};
tpl.interpolate(&hashmap! {
"ret_type" => ret_type,
"doref" => doref.into(),
"name" => call_name,
"generic" => generic.into(),
"args" => args.into(),
})
}
fn cpp_method_return(&self) -> Cow<str> {
let return_type = self.return_type();
if return_type.is_void() {
"return Ok();".into()
} else if return_type.is_by_ptr() && !self.as_constructor().is_some() {
let out = if let Some(cls) = return_type.source().as_class() {
if cls.is_abstract() {
Some(format!("return Ok<{ext}>(ret);", ext=return_type.cpp_extern_return()))
} else {
None
}
} else {
None
};
out.unwrap_or_else(|| format!("return Ok<{ext}>(new {typ}(ret));", ext=return_type.cpp_extern_return(), typ=return_type.cpp_full())).into()
} else if return_type.is_cv_string() || return_type.is_std_string() {
format!("return Ok<{ext}>(ocvrs_create_string(ret.c_str()));", ext=return_type.cpp_extern_return()).into()
} else if return_type.is_char_ptr_string() {
format!("return Ok<{ext}>(ocvrs_create_string(ret));", ext=return_type.cpp_extern_return()).into()
} else {
format!("return Ok<{ext}>(ret);", ext=return_type.cpp_extern_return()).into()
}
}
fn pre_post_arg_handle(mut arg: String, args: &mut Vec<String>) {
if !arg.is_empty() {
arg.push(';');
args.push(arg);
}
}
pub fn gen_rust_with_name(&self, name: &str, opencv_version: &str) -> String {
static TPL: Lazy<CompiledInterpolation> = Lazy::new(
|| include_str!("../tpl/func/rust.tpl.rs").compile_interpolation()
);
static TPL_NO_FAIL: Lazy<CompiledInterpolation> = Lazy::new(
|| include_str!("../tpl/func/rust_no_fail.tpl.rs").compile_interpolation()
);
static RET_MAP_STRING: Lazy<CompiledInterpolation> = Lazy::new(
|| include_str!("../tpl/func/rust_ret_map_string.tpl.rs").compile_interpolation()
);
static RET_MAP_BY_PTR: Lazy<CompiledInterpolation> = Lazy::new(
|| include_str!("../tpl/func/rust_ret_map_by_ptr.tpl.rs").compile_interpolation()
);
static RET_MAP_RAW_PTR: Lazy<CompiledInterpolation> = Lazy::new(
|| include_str!("../tpl/func/rust_ret_map_raw_ptr.tpl.rs").compile_interpolation()
);
let args = Field::rust_disambiguate_names(self.arguments()).collect::<Vec<_>>();
let as_instance_method = self.as_instance_method();
let is_const = self.is_const();
let is_infallible = self.is_infallible();
let mut decl_args = Vec::with_capacity(args.len());
let mut call_args = Vec::with_capacity(args.len());
let mut forward_args = Vec::with_capacity(args.len());
let mut pre_call_args = Vec::with_capacity(args.len());
let mut post_call_args = Vec::with_capacity(args.len());
if let Some(cls) = &as_instance_method {
decl_args.push(cls.type_ref().rust_self_func_decl(is_const));
call_args.push(cls.type_ref().rust_self_func_call(is_const));
}
let mut callback_arg_name: Option<String> = None;
for (name, arg) in args {
let type_ref = arg.type_ref();
if arg.is_user_data() {
Self::pre_post_arg_handle(
type_ref.rust_userdata_pre_call(&name, callback_arg_name.as_ref().map(|x| x.as_str()).expect("Can't get name of the callback arg")),
&mut pre_call_args
);
} else {
if type_ref.as_function().is_some() {
callback_arg_name = Some(name.clone());
}
if !arg.as_slice_len().is_some() {
decl_args.push(type_ref.rust_arg_func_decl(&name));
}
Self::pre_post_arg_handle(type_ref.rust_arg_pre_call(&name, is_infallible), &mut pre_call_args);
}
if let Some((slice_arg, len_div)) = arg.as_slice_len() {
let slice_call = if len_div > 1 {
format!("({slice_arg}.len() / {len_div}) as _", slice_arg=slice_arg, len_div=len_div)
} else {
format!("{slice_arg}.len() as _", slice_arg=slice_arg)
};
call_args.push(slice_call);
} else {
call_args.push(type_ref.rust_arg_func_call(&name));
}
forward_args.push(type_ref.rust_arg_forward(&name));
Self::pre_post_arg_handle(type_ref.rust_arg_post_call(&name, is_infallible), &mut post_call_args);
}
let doc_comment = self.rendered_doc_comment(opencv_version);
let debug = get_debug(self);
let visibility = if let Some(cls) = as_instance_method {
if cls.is_trait() {
""
} else {
"pub "
}
} else {
"pub "
};
let identifier = self.identifier();
let is_safe = !settings::FUNC_UNSAFE.contains(identifier.as_ref());
let return_type = self.return_type();
let return_type_func_decl = return_type.rust_return_func_decl();
let mut prefix = String::new();
let mut suffix = String::new();
if !post_call_args.is_empty() {
post_call_args.push("out".into());
prefix.push_str("let out = ");
suffix.push(';');
}
let decl_args = decl_args.join(", ");
let pre_call_args = pre_call_args.join("\n");
let call_args = call_args.join(", ");
let forward_args = forward_args.join(", ");
let post_call_args = post_call_args.join("\n");
let mut templ_vars = hashmap! {
"doc_comment" => doc_comment.as_str(),
"debug" => &debug,
"visibility" => &visibility,
"unsafety_decl" => if is_safe { "" } else { "unsafe " },
"name" => name,
"generic_decl" => "",
"decl_args" => &decl_args,
"rv_rust_full" => return_type_func_decl.as_ref(),
"pre_call_args" => &pre_call_args,
"prefix" => &prefix,
"unsafety_call" => if is_safe { "unsafe " } else { "" },
"identifier" => identifier.as_ref(),
"call_args" => &call_args,
"forward_args" => &forward_args,
"suffix" => &suffix,
"post_call_args" => &post_call_args,
};
let ret_map = if return_type.is_string() {
RET_MAP_STRING.interpolate(&templ_vars)
} else if return_type.is_by_ptr() {
RET_MAP_BY_PTR.interpolate(&templ_vars)
} else if !return_type.is_void_ptr() && (return_type.as_pointer().is_some() || return_type.as_fixed_array().is_some()) {
if return_type.is_const() {
templ_vars.insert("ptr_call", "ref");
} else {
templ_vars.insert("ptr_call", "mut");
}
RET_MAP_RAW_PTR.interpolate(&templ_vars)
} else {
"".to_string()
};
templ_vars.insert("ret_map", &ret_map);
let tpl = if let Some(tpl) = settings::FUNC_MANUAL.get(identifier.as_ref()) {
tpl
} else if is_infallible {
&TPL_NO_FAIL
} else {
&TPL
};
tpl.interpolate(&templ_vars)
}
}
impl<'tu> EntityElement<'tu> for Func<'tu, '_> {
fn entity(&self) -> Entity<'tu> {
self.entity
}
}
impl Element for Func<'_, '_> {
fn is_excluded(&self) -> bool {
let identifier = self.identifier();
if settings::FUNC_MANUAL.contains_key(identifier.as_ref()) || settings::FUNC_SPECIALIZE.contains_key(identifier.as_ref()) {
return false;
}
DefaultElement::is_excluded(self)
|| self.is_generic()
|| if let Some(cls) = self.as_constructor() {
cls.is_abstract()
} else {
false
}
}
fn is_ignored(&self) -> bool {
let identifier = self.identifier();
if settings::FUNC_MANUAL.contains_key(identifier.as_ref()) || settings::FUNC_SPECIALIZE.contains_key(identifier.as_ref()) {
return false;
}
DefaultElement::is_ignored(self)
|| self.entity.get_availability() == Availability::Unavailable
|| self.as_operator().map_or(false, |(.., op)| op == OperatorKind::Unsupported)
|| self.arguments().into_iter().any(|a| a.type_ref().is_ignored())
|| {
let ret = self.return_type();
ret.is_ignored() || ret.as_class().map_or(false, |cls| cls.is_abstract())
}
|| if let Some(&"-") = settings::FUNC_RENAME.get(identifier.as_ref()) {
true
} else {
false
}
}
fn is_system(&self) -> bool {
DefaultElement::is_system(self)
}
fn is_public(&self) -> bool {
DefaultElement::is_public(self)
}
fn identifier(&self) -> Cow<str> {
let mut out: String = DefaultElement::identifier(self).into_owned();
if let Some(spec) = self.as_specialized() {
for typ in spec.values() {
out.push('_');
let mut typ = typ.to_string();
typ.cleanup_name();
out.push_str(&typ);
}
}
if self.is_const() {
out += "_const";
}
for arg in self.arguments() {
out.push('_');
out += &arg.type_ref().cpp_safe_id();
}
out.into()
}
fn usr(&self) -> Cow<str> {
DefaultElement::usr(self)
}
fn rendered_doc_comment_with_prefix(&self, prefix: &str, opencv_version: &str) -> String {
let mut comment = self.entity.get_comment().unwrap_or_default();
const OVERLOAD: &str = "@overload";
if let Some(idx) = comment.find(OVERLOAD) {
let rep = if let Some(copy) = self.gen_env.get_func_comment(self.entity.cpp_fullname().as_ref()) {
format!("{}\n\n## Overloaded parameters\n", copy)
} else {
"".to_string()
};
comment.replace_range(idx..idx + OVERLOAD.len(), &rep);
}
static COPY_BRIEF: Lazy<Regex> = Lazy::new(|| Regex::new(r#"@copybrief\s+(\w+)"#).unwrap());
comment.replace_in_place_regex_cb(©_BRIEF, |comment, caps| {
let copy_name = caps.get(1).map(|(s, e)| &comment[s..e]).expect("Impossible");
let mut copy_full_name = self.cpp_namespace().into_owned();
copy_full_name += "::";
copy_full_name += copy_name;
if let Some(copy) = self.gen_env.get_func_comment(©_full_name) {
Some(copy.into())
} else {
Some("".into())
}
});
comment::render_doc_comment_with_processor(&comment, prefix, opencv_version, |out| {
let mut default_args_comment = String::with_capacity(1024);
for arg in self.arguments() {
if let Some(def_val) = arg.default_value() {
if default_args_comment.is_empty() {
default_args_comment += "## C++ default parameters";
}
default_args_comment += &format!("\n* {name}: {val}", name=arg.rust_leafname(), val=def_val);
}
}
if !default_args_comment.is_empty() {
if !out.is_empty() {
out.push_str("\n\n");
}
out.push_str(&default_args_comment);
}
})
}
fn cpp_namespace(&self) -> Cow<str> {
DefaultElement::cpp_namespace(self)
}
fn cpp_localname(&self) -> Cow<str> {
if self.as_conversion_method().is_some() {
format!("operator {}", self.return_type().cpp_full()).into()
} else if self.as_field_setter().is_some() {
let name = DefaultElement::cpp_localname(self);
format!("set{}{}", name[..1].to_uppercase(), &name[1..]).into()
} else {
DefaultElement::cpp_localname(self)
}
}
fn rust_module(&self) -> Cow<str> {
DefaultElement::rust_module(self)
}
fn rust_leafname(&self) -> Cow<str> {
let cpp_name = if let Some(name) = self.gen_env.get_export_config(self.entity).and_then(|c| c.rename.as_ref()) {
name.into()
} else {
self.cpp_localname()
};
let rust_name = if let Some(cls) = self.as_constructor() {
let args = self.arguments();
'ctor_name: loop {
if args.is_empty() {
break 'ctor_name "default";
} else if args.len() == 1 {
let arg_typeref = args[0].type_ref();
let class_arg = arg_typeref.as_reference().and_then(|typ| {
if let Some(ptr) = typ.as_smart_ptr() {
ptr.pointee()
} else {
typ
}.as_class()
});
if let Some(other) = class_arg {
if cls == other {
break 'ctor_name if arg_typeref.is_const() {
"copy"
} else {
"copy_mut"
};
}
}
}
break 'ctor_name "new";
}.into()
} else if let Some(..) = self.as_conversion_method() {
let mut name: String = self.return_type().rust_local().into_owned();
name.cleanup_name();
format!("to_{}", name).into()
} else if let Some((.., kind)) = self.as_operator() {
if cpp_name.starts_with("operator") {
match kind {
OperatorKind::Index => {
if self.is_const() {
"get".into()
} else {
"get_mut".into()
}
}
OperatorKind::Unsupported => {
cpp_name
}
}
} else {
cpp_name
}
} else {
cpp_name
};
if let Some(&name) = settings::FUNC_RENAME.get(self.identifier().as_ref()) {
if name.contains('+') {
reserved_rename(name.replace('+', rust_name.as_ref()).to_snake_case().into())
} else {
name.into()
}
} else {
reserved_rename(rust_name.to_snake_case().into())
}
}
fn rust_localname(&self) -> Cow<str> {
DefaultElement::rust_localname(self)
}
}
impl GeneratedElement for Func<'_, '_> {
fn element_safe_id(&self) -> String {
format!("{}-{}", self.rust_module(), self.rust_localname())
}
fn gen_rust(&self, opencv_version: &str) -> String {
let name = if let Some(name_hint) = self.name_hint {
name_hint.into()
} else {
self.rust_leafname()
};
self.gen_rust_with_name(&name, opencv_version)
}
fn gen_rust_exports(&self) -> String {
static TPL: Lazy<CompiledInterpolation> = Lazy::new(
|| include_str!("../tpl/func/rust_extern.tpl.rs").compile_interpolation()
);
if settings::FUNC_MANUAL.contains_key(self.identifier().as_ref()) {
return "".to_string()
}
let mut args = vec![];
if let Some(cls) = self.as_instance_method() {
args.push(cls.type_ref().rust_extern_self_func_decl(self.is_const()));
}
for arg in self.arguments() {
args.push(arg.type_ref().rust_extern_arg_func_decl(&arg.rust_leafname()))
}
let return_type = self.return_type();
let return_wrapper_type = return_type.rust_extern_return_wrapper_full();
TPL.interpolate(&hashmap! {
"debug" => Cow::Owned(get_debug(self)),
"identifier" => self.identifier(),
"args" => args.join(", ").into(),
"return_wrapper_type" => return_wrapper_type,
})
}
fn gen_cpp(&self) -> String {
static TPL: Lazy<CompiledInterpolation> = Lazy::new(
|| include_str!("../tpl/func/cpp.tpl.cpp").compile_interpolation()
);
if settings::FUNC_MANUAL.contains_key(self.identifier().as_ref()) {
return "".to_string()
}
let args = Field::cpp_disambiguate_names(self.arguments()).collect::<Vec<_>>();
let mut decl_args = Vec::with_capacity(args.len());
let mut pre_call_args = Vec::with_capacity(args.len());
let mut post_call_args = Vec::with_capacity(args.len());
if let Some(cls) = self.as_instance_method() {
decl_args.push(cls.type_ref().cpp_self_func_decl(self.is_const()));
}
for (name, arg) in args {
let type_ref = arg.type_ref();
decl_args.push(type_ref.cpp_arg_func_decl(&name));
let mut pre_call_arg = type_ref.cpp_arg_pre_call(&name);
if !pre_call_arg.is_empty() {
pre_call_arg.push(';');
pre_call_args.push(pre_call_arg);
}
let mut post_call_arg = type_ref.cpp_arg_post_call(&name);
if !post_call_arg.is_empty() {
post_call_arg.push(';');
post_call_args.push(post_call_arg);
}
}
let return_type = self.return_type();
TPL.interpolate(&hashmap! {
"debug" => get_debug(self).into(),
"return_wrapper_type" => return_type.cpp_extern_return_wrapper_full(),
"identifier" => self.identifier(),
"decl_args" => decl_args.join(", ").into(),
"pre_call_args" => pre_call_args.join("\n").into(),
"call" => self.cpp_call_invoke().into(),
"post_call_args" => post_call_args.join("\n").into(),
"return" => self.cpp_method_return(),
})
}
}
impl fmt::Display for Func<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.entity.get_display_name().expect("Can't get display name"))
}
}
impl fmt::Debug for Func<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut debug_struct = f.debug_struct("Func");
self.update_debug_struct(&mut debug_struct)
.field("export_config", &self.gen_env.get_export_config(self.entity))
.field("is_const", &self.is_const())
.field("type_hint", &self.type_hint)
.field("kind", &self.kind())
.field("return_type", &self.return_type())
.field("arguments", &self.arguments())
.finish()
}
}