use std::rc::Rc;
use log::{debug, trace, warn};
use petgraph::Direction;
use syn::Type;
use crate::{
cpp::{merge_c_types, merge_rule, CppContext, CppForeignTypeInfo, MergeCItemsFlags},
error::{DiagnosticError, Result, SourceIdSpan},
typemap::{
ast::{DisplayToTokens, TyParamsSubstList},
ty::{ForeignType, RustType, TraitNamesSet},
ExpandedFType, MapToForeignFlag, TypeMapConvRuleInfoExpanderHelper, FROM_VAR_TEMPLATE,
},
types::ForeignClassInfo,
TypeMap,
};
pub(in crate::cpp) fn map_type(
ctx: &mut CppContext,
arg_ty: &RustType,
direction: Direction,
arg_ty_span: SourceIdSpan,
) -> Result<CppForeignTypeInfo> {
debug!("map_type: arg_ty {}, direction {:?}", arg_ty, direction);
let ftype = do_map_type(ctx, arg_ty, direction, arg_ty_span)?;
CppForeignTypeInfo::try_new(ctx, direction, ftype)
}
fn do_map_type(
ctx: &mut CppContext,
arg_ty: &RustType,
direction: Direction,
arg_ty_span: SourceIdSpan,
) -> Result<ForeignType> {
debug!("do_map_type: arg_ty {}, direction {:?}", arg_ty, direction);
if let Some(ftype) = ctx.conv_map.map_through_conversation_to_foreign(
arg_ty.to_idx(),
direction,
MapToForeignFlag::FastSearch,
arg_ty_span,
calc_this_type_for_method,
) {
return Ok(ftype);
}
let idx_subst_map: Option<(Rc<_>, TyParamsSubstList)> =
ctx.conv_map.generic_rules().iter().find_map(|grule| {
grule
.is_ty_subst_of_my_generic_rtype(&arg_ty.ty, direction, |ty, traits| -> bool {
is_ty_implement_traits(ctx.conv_map, ty, traits)
})
.map(|sm| (grule.clone(), sm.into()))
});
if let Some((grule, subst_list)) = idx_subst_map {
debug!(
"do_map_type: we found generic rule for {}: {:?}",
arg_ty, subst_list
);
let subst_map = subst_list.as_slice().into();
let c_types = grule
.subst_generic_params_to_c_items(
&subst_map,
&mut CppContextForArg {
ctx,
arg_ty_span,
direction,
},
)
.map_err(|err| {
err.add_span_note(
(grule.src_id, grule.span),
"subst. of generic params in define_c_type failed",
)
})?;
if let Some(c_types) = c_types {
merge_c_types(
ctx,
c_types,
MergeCItemsFlags::DefineAlsoRustType,
grule.src_id,
)?;
}
let new_rule = grule
.subst_generic_params(
subst_map,
direction,
&mut CppContextForArg {
ctx,
arg_ty_span,
direction,
},
)
.map_err(|err| {
err.add_span_note(
(grule.src_id, grule.span),
"subst. of generic params into rule failed",
)
})?;
debug_assert!(!new_rule.is_empty());
merge_rule(ctx, new_rule)?;
if let Some(ftype) = ctx.conv_map.map_through_conversation_to_foreign(
arg_ty.to_idx(),
direction,
MapToForeignFlag::FullSearch,
arg_ty_span,
calc_this_type_for_method,
) {
return Ok(ftype);
}
} else if let Some(ftype) = ctx.conv_map.map_through_conversation_to_foreign(
arg_ty.to_idx(),
direction,
MapToForeignFlag::FullSearch,
arg_ty_span,
calc_this_type_for_method,
) {
return Ok(ftype);
}
match direction {
Direction::Outgoing => Err(DiagnosticError::new2(
arg_ty_span,
format!(
"Do not know conversation from \
such rust type '{}' to C++ type",
arg_ty
),
)),
Direction::Incoming => Err(DiagnosticError::new2(
arg_ty_span,
format!(
"Do not know conversation from C++ type \
to such rust type '{}'",
arg_ty
),
)),
}
}
struct CppContextForArg<'a, 'b> {
ctx: &'a mut CppContext<'b>,
arg_ty_span: SourceIdSpan,
direction: Direction,
}
impl<'a, 'b> CppContextForArg<'a, 'b> {
fn arg_direction(&self, param1: Option<&str>) -> Result<Direction> {
match param1 {
Some("output") => Ok(Direction::Outgoing),
Some("input") => Ok(Direction::Incoming),
None => Ok(self.direction),
Some(param) => Err(DiagnosticError::new2(
self.arg_ty_span,
format!("Invalid argument '{}' for swig_f_type", param),
)),
}
}
}
impl<'a, 'b> TypeMapConvRuleInfoExpanderHelper for CppContextForArg<'a, 'b> {
fn swig_i_type(&mut self, ty: &syn::Type, opt_arg: Option<&str>) -> Result<syn::Type> {
let rust_ty = self
.ctx
.conv_map
.find_or_alloc_rust_type(ty, self.arg_ty_span.0);
let direction = self.arg_direction(opt_arg)?;
let f_info = map_type(self.ctx, &rust_ty, direction, self.arg_ty_span)?;
trace!("swig_i_type return {}", f_info.base.correspoding_rust_type);
Ok(f_info.base.correspoding_rust_type.ty.clone())
}
fn swig_from_rust_to_i_type(
&mut self,
ty: &syn::Type,
in_var_name: &str,
out_var_name: &str,
) -> Result<String> {
let rust_ty = self
.ctx
.conv_map
.find_or_alloc_rust_type(ty, self.arg_ty_span.0);
let f_info = map_type(self.ctx, &rust_ty, Direction::Outgoing, self.arg_ty_span)?;
let (mut conv_deps, conv_code) = self.ctx.conv_map.convert_rust_types(
rust_ty.to_idx(),
f_info.base.correspoding_rust_type.to_idx(),
in_var_name,
out_var_name,
"#error",
self.arg_ty_span,
)?;
self.ctx.rust_code.append(&mut conv_deps);
Ok(conv_code)
}
fn swig_from_i_type_to_rust(
&mut self,
ty: &syn::Type,
in_var_name: &str,
out_var_name: &str,
) -> Result<String> {
let rust_ty = self
.ctx
.conv_map
.find_or_alloc_rust_type(ty, self.arg_ty_span.0);
let f_info = map_type(self.ctx, &rust_ty, Direction::Incoming, self.arg_ty_span)?;
let (mut conv_deps, conv_code) = self.ctx.conv_map.convert_rust_types(
f_info.base.correspoding_rust_type.to_idx(),
rust_ty.to_idx(),
in_var_name,
out_var_name,
"#error",
self.arg_ty_span,
)?;
self.ctx.rust_code.append(&mut conv_deps);
Ok(conv_code)
}
fn swig_f_type(&mut self, ty: &syn::Type, param1: Option<&str>) -> Result<ExpandedFType> {
let rust_ty = self
.ctx
.conv_map
.find_or_alloc_rust_type(ty, self.arg_ty_span.0);
let direction = self.arg_direction(param1)?;
let f_info = map_type(self.ctx, &rust_ty, direction, self.arg_ty_span)?;
let fname = if let Some(ref cpp_conv) = f_info.cpp_converter {
cpp_conv.typename.as_str()
} else {
f_info.base.name.as_str()
};
Ok(ExpandedFType {
name: fname.replace("struct ", "").replace("union ", "").into(),
provides_by_module: f_info.provides_by_module.clone(),
})
}
fn swig_foreign_to_i_type(&mut self, ty: &syn::Type, var_name: &str) -> Result<String> {
let rust_ty = self
.ctx
.conv_map
.find_or_alloc_rust_type(ty, self.arg_ty_span.0);
let f_info = map_type(self.ctx, &rust_ty, Direction::Incoming, self.arg_ty_span)?;
if let Some(cpp_conv) = f_info.cpp_converter {
Ok(cpp_conv
.converter
.as_str()
.replace(FROM_VAR_TEMPLATE, var_name))
} else {
Ok(var_name.into())
}
}
fn swig_foreign_from_i_type(&mut self, ty: &syn::Type, var_name: &str) -> Result<String> {
let rust_ty = self
.ctx
.conv_map
.find_or_alloc_rust_type(ty, self.arg_ty_span.0);
let f_info = map_type(self.ctx, &rust_ty, Direction::Outgoing, self.arg_ty_span)?;
if let Some(cpp_conv) = f_info.cpp_converter {
Ok(cpp_conv
.converter
.as_str()
.replace(FROM_VAR_TEMPLATE, var_name))
} else {
Ok(var_name.into())
}
}
}
pub(in crate::cpp) fn calc_this_type_for_method(
_: &TypeMap,
class: &ForeignClassInfo,
) -> Option<Type> {
class
.self_desc
.as_ref()
.map(|x| x.constructor_ret_type.clone())
}
fn is_ty_implement_traits(tmap: &TypeMap, ty: &syn::Type, traits: &TraitNamesSet) -> bool {
if let Some(rty) = tmap.ty_to_rust_type_checked(ty) {
for tname in traits.iter() {
if tname.is_ident("SwigTypeIsReprC") {
if tmap
.find_foreign_type_related_to_rust_ty(rty.to_idx())
.is_none()
{
return false;
}
} else if !rty.implements.contains_path(tname) {
return false;
}
}
true
} else {
warn!(
"is_ty_implement_traits: type {} unknown",
DisplayToTokens(ty)
);
false
}
}
pub(in crate::cpp) fn map_repr_c_type(
ctx: &mut CppContext,
rty: &RustType,
arg_ty_span: SourceIdSpan,
) -> Result<CppForeignTypeInfo> {
debug!("map_repr_c_type: rty {}", rty);
let fti = map_type(
ctx,
rty,
Direction::Incoming,
arg_ty_span,
)?;
if fti.cpp_converter.is_some() || fti.base.correspoding_rust_type.to_idx() != rty.to_idx() {
return Err(DiagnosticError::new2(
arg_ty_span,
format!(
"we map {} to {}, but this is not repr(C) type",
rty, fti.base.name
),
));
}
Ok(fti)
}