mod analysis;
mod api;
mod apivec;
mod codegen_cpp;
mod codegen_rs;
#[cfg(test)]
mod conversion_tests;
mod convert_error;
mod doc_attr;
mod error_reporter;
mod parse;
mod type_helpers;
mod utilities;
pub(crate) use super::parse_callbacks::CppOriginalName;
use analysis::fun::FnAnalyzer;
use autocxx_bindgen::callbacks::Visibility as CppVisibility;
use autocxx_parser::IncludeCppConfig;
pub(crate) use codegen_cpp::CppCodeGenerator;
pub(crate) use convert_error::ConvertError;
use convert_error::{ConvertErrorFromCpp, ConvertErrorWithContext, ErrorContext};
use itertools::Itertools;
use syn::{Item, ItemMod};
use crate::{
types::QualifiedName, CodegenOptions, CppFilePair, ParseCallbackResults, UnsafePolicy,
};
use self::{
analysis::{
abstract_types::{discard_ignored_functions, mark_types_abstract},
allocators::create_alloc_and_frees,
casts::add_casts,
check_names,
constructor_deps::decorate_types_with_constructor_deps,
gc::filter_apis_by_following_edges_from_allowlist,
pod::analyze_pod_apis,
remove_ignored::filter_apis_by_ignored_dependents,
replace_hopeless_typedef_targets,
tdef::convert_typedef_targets,
},
api::AnalysisPhase,
apivec::ApiVec,
codegen_rs::RsCodeGenerator,
parse::ParseBindgen,
};
const LOG_APIS: bool = true;
pub(crate) struct BridgeConverter<'a> {
include_list: &'a [String],
config: &'a IncludeCppConfig,
}
pub(crate) struct CodegenResults {
pub(crate) rs: Vec<Item>,
pub(crate) cpp: Option<CppFilePair>,
pub(crate) cxxgen_header_name: String,
}
impl<'a> BridgeConverter<'a> {
pub fn new(include_list: &'a [String], config: &'a IncludeCppConfig) -> Self {
Self {
include_list,
config,
}
}
fn dump_apis<T: AnalysisPhase>(label: &str, apis: &ApiVec<T>) {
if LOG_APIS {
log::info!(
"##### APIs after {}:\n{}",
label,
apis.iter()
.map(|api| { format!(" {api:?}") })
.sorted()
.join("\n")
)
}
}
pub(crate) fn convert(
&self,
bindgen_mod: ItemMod,
parse_callback_results: ParseCallbackResults,
unsafe_policy: UnsafePolicy,
inclusions: String,
codegen_options: &CodegenOptions,
source_file_contents: &str,
) -> Result<CodegenResults, ConvertError> {
match &bindgen_mod.content {
None => Err(ConvertError::NoContent),
Some((_, items)) => {
let parser = ParseBindgen::new(self.config, &parse_callback_results);
let apis = parser.parse_items(items, source_file_contents)?;
Self::dump_apis("parsing", &apis);
let apis = convert_typedef_targets(self.config, apis, &parse_callback_results);
Self::dump_apis("typedefs", &apis);
let analyzed_apis = analyze_pod_apis(apis, self.config, &parse_callback_results)
.map_err(ConvertError::Cpp)?;
Self::dump_apis("pod analysis", &analyzed_apis);
let analyzed_apis = replace_hopeless_typedef_targets(self.config, analyzed_apis);
let analyzed_apis = add_casts(analyzed_apis);
let analyzed_apis = create_alloc_and_frees(analyzed_apis);
Self::dump_apis("adding casts", &analyzed_apis);
let analyzed_apis = FnAnalyzer::analyze_functions(
analyzed_apis,
&unsafe_policy,
self.config,
codegen_options.force_wrapper_gen,
);
Self::dump_apis("analyze fns", &analyzed_apis);
let analyzed_apis = mark_types_abstract(analyzed_apis);
Self::dump_apis("marking abstract", &analyzed_apis);
let analyzed_apis = decorate_types_with_constructor_deps(analyzed_apis);
Self::dump_apis("adding constructor deps", &analyzed_apis);
let analyzed_apis = discard_ignored_functions(analyzed_apis);
Self::dump_apis("ignoring ignorable fns", &analyzed_apis);
let analyzed_apis = check_names(analyzed_apis);
let analyzed_apis = filter_apis_by_ignored_dependents(analyzed_apis);
Self::dump_apis("removing ignored dependents", &analyzed_apis);
let mut analyzed_apis =
filter_apis_by_following_edges_from_allowlist(analyzed_apis, self.config);
analysis::ctypes::append_ctype_information(&mut analyzed_apis);
Self::dump_apis("GC", &analyzed_apis);
let cxxgen_header_name = codegen_options
.cpp_codegen_options
.cxxgen_header_namer
.name_header();
let cpp = CppCodeGenerator::generate_cpp_code(
inclusions,
&analyzed_apis,
self.config,
&codegen_options.cpp_codegen_options,
&cxxgen_header_name,
)
.map_err(ConvertError::Cpp)?;
let rs = RsCodeGenerator::generate_rs_code(
analyzed_apis,
&unsafe_policy,
self.include_list,
bindgen_mod,
self.config,
cpp.as_ref().map(|file_pair| file_pair.header_name.clone()),
);
Ok(CodegenResults {
rs,
cpp,
cxxgen_header_name,
})
}
}
}
}
#[derive(PartialEq, PartialOrd, Eq, Hash, Clone, Debug)]
pub struct CppEffectiveName(pub(crate) String);
impl CppEffectiveName {
fn from_cpp_name_and_rust_name(cpp_name: Option<&CppOriginalName>, rust_name: &str) -> Self {
cpp_name
.map(|cpp| cpp.to_effective_name())
.unwrap_or(Self(rust_name.to_string()))
}
fn from_api_details(original_name: &Option<CppOriginalName>, api_name: &QualifiedName) -> Self {
Self::from_cpp_name_and_rust_name(original_name.as_ref(), api_name.get_final_item())
}
fn to_string_for_cpp_generation(&self) -> &str {
&self.0
}
fn from_subclass_function_name(rust_call_name: String) -> CppEffectiveName {
Self(rust_call_name)
}
fn from_cxxbridge_name(cxxbridge_name: &crate::minisyn::Ident) -> CppEffectiveName {
Self(cxxbridge_name.to_string())
}
fn from_rust_name(rust_name: String) -> CppEffectiveName {
Self(rust_name)
}
fn from_fully_qualified_name_for_subclass(to_cpp_name: &str) -> CppEffectiveName {
Self(to_cpp_name.to_string())
}
fn final_segment_if_any(&self) -> Option<&str> {
self.0.rsplit_once("::").map(|(_, suffix)| suffix)
}
fn is_nested(&self) -> bool {
self.0.contains("::")
}
}
fn check_for_fatal_attrs(
callback_results: &ParseCallbackResults,
name: &QualifiedName,
) -> Result<(), ConvertErrorWithContext> {
if callback_results.discards_template_param(name) {
Err(ConvertErrorWithContext(
ConvertErrorFromCpp::UnusedTemplateParam,
Some(ErrorContext::new_for_item(name.get_final_ident())),
))
} else if !matches!(
callback_results.get_cpp_visibility(name),
CppVisibility::Public
) {
Err(ConvertErrorWithContext(
ConvertErrorFromCpp::NonPublicNestedType,
Some(ErrorContext::new_for_item(name.get_final_ident())),
))
} else {
Ok(())
}
}