use crate::types::{Namespace, TypeName};
use proc_macro2::TokenStream;
use std::{
collections::{HashMap, HashSet},
fmt::Display,
};
use syn::{ForeignItemFn, Ident, ImplItem, Item, ItemConst, ItemType};
use super::{codegen_cpp::AdditionalNeed, parse::type_converter::TypeConverter};
#[derive(Debug, Clone)]
pub enum ConvertError {
NoContent,
UnsafePodType(String),
UnexpectedForeignItem,
UnexpectedOuterItem,
UnexpectedItemInMod,
ComplexTypedefTarget(String),
UnexpectedThisType(Namespace, String),
UnsupportedBuiltInType(TypeName),
VirtualThisType(Namespace, String),
ConflictingTemplatedArgsWithTypedef(TypeName),
UnacceptableParam(String),
NotOneInputReference(String),
UnsupportedType(String),
UnknownType(String),
}
impl Display for ConvertError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConvertError::NoContent => write!(f, "The initial run of 'bindgen' did not generate any content. This might be because none of the requested items for generation could be converted.")?,
ConvertError::UnsafePodType(err) => write!(f, "An item was requested using 'generate_pod' which was not safe to hold by value in Rust. {}", err)?,
ConvertError::UnexpectedForeignItem => write!(f, "Bindgen generated some unexpected code in a foreign mod section. You may have specified something in a 'generate' directive which is not currently compatible with autocxx.")?,
ConvertError::UnexpectedOuterItem => write!(f, "Bindgen generated some unexpected code in its outermost mod section. You may have specified something in a 'generate' directive which is not currently compatible with autocxx.")?,
ConvertError::UnexpectedItemInMod => write!(f, "Bindgen generated some unexpected code in an inner namespace mod. You may have specified something in a 'generate' directive which is not currently compatible with autocxx.")?,
ConvertError::ComplexTypedefTarget(ty) => write!(f, "autocxx was unable to produce a typdef pointing to the complex type {}.", ty)?,
ConvertError::UnexpectedThisType(ns, fn_name) => write!(f, "Unexpected type for 'this' in the function {}{}.", fn_name, ns.to_display_suffix())?,
ConvertError::UnsupportedBuiltInType(ty) => write!(f, "autocxx does not yet know how to support the built-in C++ type {} - please raise an issue on github", ty.to_cpp_name())?,
ConvertError::VirtualThisType(ns, fn_name) => write!(f, "Member function encountered where the 'this' type is 'void*', but we were unable to recognize which type that corresponds to. Function {}{}.", fn_name, ns.to_display_suffix())?,
ConvertError::ConflictingTemplatedArgsWithTypedef(tn) => write!(f, "Type {} has templated arguments and so does the typedef to which it points", tn)?,
ConvertError::UnacceptableParam(fn_name) => write!(f, "Function {} has a parameter or return type which is either on the blocklist or a forward declaration", fn_name)?,
ConvertError::NotOneInputReference(fn_name) => write!(f, "Function {} has a return reference parameter, but 0 or >1 input reference parameters, so the lifetime of the output reference cannot be deduced.", fn_name)?,
ConvertError::UnsupportedType(ty_desc) => write!(f, "Encountered type not yet supported by autocxx: {}", ty_desc)?,
ConvertError::UnknownType(ty_desc) => write!(f, "Encountered type not yet known by autocxx: {}", ty_desc)?,
}
Ok(())
}
}
impl ConvertError {
pub(crate) fn is_ignorable(&self) -> bool {
matches!(
self,
ConvertError::VirtualThisType(..)
| ConvertError::UnsupportedBuiltInType(..)
| ConvertError::UnacceptableParam(..)
| ConvertError::NotOneInputReference(..)
| ConvertError::UnsupportedType(..)
)
}
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub(crate) enum TypeKind {
Pod, NonPod, ForwardDeclaration, }
#[derive(Clone)]
pub(crate) enum Use {
Unused,
Used,
UsedWithAlias(Ident),
}
pub(crate) struct TypeApiDetails {
pub(crate) fulltypath: Vec<Ident>,
pub(crate) final_ident: Ident,
pub(crate) tynamestring: String,
}
pub(crate) struct ImplBlockDetails {
pub(crate) item: ImplItem,
pub(crate) ty: Ident,
}
#[derive(Clone)]
pub(crate) struct FuncToConvert {
pub(crate) item: ForeignItemFn,
pub(crate) virtual_this_type: Option<TypeName>,
pub(crate) self_ty: Option<TypeName>,
}
pub(crate) trait ApiAnalysis {
type TypeAnalysis;
type FunAnalysis;
}
pub(crate) struct NullAnalysis;
impl ApiAnalysis for NullAnalysis {
type TypeAnalysis = ();
type FunAnalysis = ();
}
pub(crate) enum ApiDetail<T: ApiAnalysis> {
ConcreteType(TypeApiDetails),
StringConstructor,
Function {
fun: FuncToConvert,
analysis: T::FunAnalysis,
},
Const { const_item: ItemConst },
Typedef { type_item: ItemType },
Type {
ty_details: TypeApiDetails,
for_extern_c_ts: TokenStream,
is_forward_declaration: bool,
bindgen_mod_item: Option<Item>,
analysis: T::TypeAnalysis,
},
CType { id: Ident },
}
pub(crate) struct Api<T: ApiAnalysis> {
pub(crate) ns: Namespace,
pub(crate) id: Ident,
pub(crate) use_stmt: Use,
pub(crate) deps: HashSet<TypeName>,
pub(crate) additional_cpp: Option<AdditionalNeed>,
pub(crate) detail: ApiDetail<T>,
}
pub(crate) type UnanalyzedApi = Api<NullAnalysis>;
impl<T: ApiAnalysis> Api<T> {
pub(crate) fn typename(&self) -> TypeName {
TypeName::new(&self.ns, &self.id.to_string())
}
}
pub(crate) struct ParseResults {
pub(crate) apis: Vec<UnanalyzedApi>,
pub(crate) type_converter: TypeConverter,
pub(crate) use_stmts_by_mod: HashMap<Namespace, Vec<Item>>,
}