use std::collections::{HashMap, HashSet};
use syn::parse_quote;
use crate::conversion::analysis::fun::ReceiverMutability;
use crate::conversion::analysis::pod::PodPhase;
use crate::conversion::api::{RustSubclassFnDetails, SubclassName};
use crate::{
conversion::{
analysis::fun::{
function_wrapper::{
CppFunction, CppFunctionBody, CppFunctionKind, TypeConversionPolicy,
},
ArgumentAnalysis,
},
api::{Api, ApiName},
},
types::{make_ident, Namespace, QualifiedName},
};
use super::{FnAnalysis, FnKind, FnPhase, MethodKind};
pub(super) fn subclasses_by_superclass(
apis: &[Api<PodPhase>],
) -> HashMap<QualifiedName, Vec<SubclassName>> {
let mut subclasses_per_superclass: HashMap<QualifiedName, Vec<SubclassName>> = HashMap::new();
for api in apis.iter() {
if let Api::Subclass { name, superclass } = api {
subclasses_per_superclass
.entry(superclass.clone())
.or_default()
.push(name.clone());
}
}
subclasses_per_superclass
}
pub(super) fn create_subclass_function(
sub: &SubclassName,
analysis: &super::FnAnalysis,
name: &ApiName,
receiver_mutability: &ReceiverMutability,
superclass: &QualifiedName,
) -> Api<FnPhase> {
let cpp = sub.cpp();
let holder_name = sub.holder();
let rust_call_name = make_ident(format!(
"{}_{}",
sub.0.name.get_final_item(),
name.name.get_final_item()
));
let params = std::iter::once(parse_quote! {
me: & #holder_name
})
.chain(analysis.params.iter().skip(1).cloned())
.collect();
let kind = if matches!(receiver_mutability, ReceiverMutability::Mutable) {
CppFunctionKind::Method
} else {
CppFunctionKind::ConstMethod
};
let super_fn_api_name = QualifiedName::new(
&Namespace::new(),
SubclassName::get_super_fn_name(&analysis.rust_name.to_string()),
);
let subclass_function: Api<FnPhase> = Api::RustSubclassFn {
name: ApiName::new_in_root_namespace(rust_call_name.clone()),
subclass: sub.clone(),
details: Box::new(RustSubclassFnDetails {
params,
ret: analysis.ret_type.clone(),
method_name: make_ident(&analysis.rust_name),
cpp_impl: CppFunction {
payload: CppFunctionBody::FunctionCall(Namespace::new(), rust_call_name),
wrapper_function_name: name.name.get_final_ident(),
return_conversion: analysis.ret_conversion.clone(),
argument_conversion: analysis
.param_details
.iter()
.skip(1)
.map(|p| p.conversion.clone())
.collect(),
kind,
pass_obs_field: true,
qualification: Some(cpp),
},
superclass: superclass.clone(),
receiver_mutability: receiver_mutability.clone(),
super_fn_api_name,
}),
};
subclass_function
}
pub(super) fn add_subclass_constructor(
sub: &SubclassName,
new_apis: &mut Vec<Api<FnPhase>>,
analysis: &super::FnAnalysis,
fun: &crate::conversion::api::FuncToConvert,
) {
let holder_name = sub.holder();
let cpp = sub.cpp();
let cpp_impl = CppFunction {
payload: CppFunctionBody::AssignSubclassHolderField,
wrapper_function_name: cpp.clone(),
return_conversion: None,
argument_conversion: vec![TypeConversionPolicy::new_unconverted(parse_quote! {
rust::Box< #holder_name >
})],
kind: CppFunctionKind::Constructor,
pass_obs_field: false,
qualification: Some(cpp.clone()),
};
new_apis.push(Api::RustSubclassConstructor {
name: ApiName::new_in_root_namespace(cpp.clone()),
subclass: sub.clone(),
cpp_impl: Box::new(cpp_impl),
is_trivial: determine_if_trivial(analysis),
});
let wrapper_name = make_ident(format!("{}_make_unique", cpp));
let mut constructor_wrapper = analysis.clone();
let id = sub.0.name.get_final_ident();
constructor_wrapper.param_details.insert(
0,
ArgumentAnalysis {
conversion: TypeConversionPolicy::box_up_subclass_holder(
parse_quote! {
autocxx::subclass::CppSubclassRustPeerHolder<super::super::super:: #id>
},
holder_name.clone(),
),
name: parse_quote! {rs_peer},
self_type: None,
was_reference: false,
deps: HashSet::new(),
is_virtual: false,
requires_unsafe: false,
is_subclass_holder: true,
},
);
constructor_wrapper
.params
.insert(0, parse_quote! { rs_peer: Box<#holder_name> });
constructor_wrapper.cxxbridge_name = wrapper_name.clone();
constructor_wrapper.ret_type = parse_quote! { -> cxx::UniquePtr < #cpp > };
constructor_wrapper.kind = FnKind::Method(
QualifiedName::new(&Namespace::new(), cpp.clone()),
MethodKind::Static,
);
constructor_wrapper.cpp_wrapper = Some(CppFunction {
payload: CppFunctionBody::Constructor,
wrapper_function_name: wrapper_name.clone(),
return_conversion: Some(TypeConversionPolicy::new_to_unique_ptr(
parse_quote! { #cpp },
)),
argument_conversion: vec![TypeConversionPolicy::new_unconverted(parse_quote! {
rust::Box< #holder_name >
})],
kind: CppFunctionKind::Function,
pass_obs_field: false,
qualification: None,
});
new_apis.push(Api::Function {
name: ApiName::new_in_root_namespace(wrapper_name),
name_for_gc: Some(sub.0.name.clone()),
analysis: constructor_wrapper,
fun: Box::new(fun.clone()),
})
}
fn determine_if_trivial(analysis: &FnAnalysis) -> bool {
let mut param_iterator = analysis.param_details.iter();
match param_iterator.next() {
None => false,
Some(pd) => pd.is_subclass_holder && param_iterator.next().is_none(),
}
}