mod byvalue_checker;
use std::collections::HashSet;
use autocxx_parser::IncludeCppConfig;
use byvalue_checker::ByValueChecker;
use syn::{ItemStruct, Type};
use crate::{
conversion::{
analysis::type_converter::{add_analysis, TypeConversionContext, TypeConverter},
api::{AnalysisPhase, Api, ApiDetail, TypeKind, TypedefKind, UnanalyzedApi},
codegen_rs::make_non_pod,
error_reporter::convert_item_apis,
ConvertError,
},
types::{Namespace, QualifiedName},
};
use super::tdef::TypedefAnalysis;
pub(crate) struct PodStructAnalysisBody {
pub(crate) kind: TypeKind,
pub(crate) bases: HashSet<QualifiedName>,
}
pub(crate) struct PodAnalysis;
impl AnalysisPhase for PodAnalysis {
type TypedefAnalysis = TypedefKind;
type StructAnalysis = PodStructAnalysisBody;
type FunAnalysis = ();
}
pub(crate) fn analyze_pod_apis(
apis: Vec<Api<TypedefAnalysis>>,
config: &IncludeCppConfig,
) -> Result<Vec<Api<PodAnalysis>>, ConvertError> {
let byvalue_checker = ByValueChecker::new_from_apis(&apis, config)?;
let mut extra_apis = Vec::new();
let mut type_converter = TypeConverter::new(config, &apis);
let mut results = Vec::new();
convert_item_apis(apis, &mut results, |api| {
analyze_pod_api(api, &byvalue_checker, &mut type_converter, &mut extra_apis).map(Some)
});
let mut more_extra_apis = Vec::new();
convert_item_apis(extra_apis, &mut results, |api| {
analyze_pod_api(
add_analysis(api),
&byvalue_checker,
&mut type_converter,
&mut more_extra_apis,
)
.map(Some)
});
assert!(more_extra_apis.is_empty());
Ok(results)
}
fn analyze_pod_api(
api: Api<TypedefAnalysis>,
byvalue_checker: &ByValueChecker,
type_converter: &mut TypeConverter,
extra_apis: &mut Vec<UnanalyzedApi>,
) -> Result<Api<PodAnalysis>, ConvertError> {
let ty_id = api.name();
let mut new_deps = api.deps;
let api_detail = match api.detail {
ApiDetail::ConcreteType {
rs_definition,
cpp_definition,
} => ApiDetail::ConcreteType {
rs_definition,
cpp_definition,
},
ApiDetail::ForwardDeclaration => ApiDetail::ForwardDeclaration,
ApiDetail::StringConstructor => ApiDetail::StringConstructor,
ApiDetail::Function { fun, analysis } => ApiDetail::Function { fun, analysis },
ApiDetail::Const { const_item } => ApiDetail::Const { const_item },
ApiDetail::Typedef { item, analysis } => ApiDetail::Typedef { item, analysis },
ApiDetail::CType { typename } => ApiDetail::CType { typename },
ApiDetail::Enum { mut item } => {
super::remove_bindgen_attrs(&mut item.attrs)?;
ApiDetail::Enum { item }
}
ApiDetail::Struct {
mut item,
analysis: _,
} => {
super::remove_bindgen_attrs(&mut item.attrs)?;
let bases = get_bases(&item);
let type_kind = if byvalue_checker.is_pod(&ty_id) {
get_struct_field_types(
type_converter,
&api.name.get_namespace(),
&item,
&mut new_deps,
extra_apis,
)?;
TypeKind::Pod
} else {
make_non_pod(&mut item);
new_deps.clear();
TypeKind::NonPod
};
ApiDetail::Struct {
item,
analysis: PodStructAnalysisBody {
kind: type_kind,
bases,
},
}
}
ApiDetail::IgnoredItem { err, ctx } => ApiDetail::IgnoredItem { err, ctx },
};
Ok(Api {
name: api.name,
cpp_name: api.cpp_name,
deps: new_deps,
detail: api_detail,
})
}
fn get_struct_field_types(
type_converter: &mut TypeConverter,
ns: &Namespace,
s: &ItemStruct,
deps: &mut HashSet<QualifiedName>,
extra_apis: &mut Vec<UnanalyzedApi>,
) -> Result<(), ConvertError> {
for f in &s.fields {
let annotated =
type_converter.convert_type(f.ty.clone(), ns, &TypeConversionContext::CxxInnerType)?;
extra_apis.extend(annotated.extra_apis);
deps.extend(annotated.types_encountered);
}
Ok(())
}
fn get_bases(item: &ItemStruct) -> HashSet<QualifiedName> {
item.fields
.iter()
.filter_map(|f| match &f.ty {
Type::Path(typ) => f
.ident
.as_ref()
.filter(|id| id.to_string().starts_with("_base"))
.map(|_| QualifiedName::from_type_path(&typ)),
_ => None,
})
.collect()
}