use crate::types::{make_ident, TypeName};
use indoc::indoc;
use lazy_static::lazy_static;
use std::collections::HashMap;
use syn::{parse_quote, Type, TypePath};
#[derive(Debug)]
enum PreludePolicy {
Exclude,
IncludeNormal,
IncludeTemplated,
}
#[derive(Debug)]
struct TypeDetails {
rs_name: String,
cpp_name: String,
by_value_safe: bool,
prelude_policy: PreludePolicy,
de_referencicate: bool,
is_ctype: bool,
is_cxx_container: bool,
extra_non_canonical_name: Option<String>,
}
impl TypeDetails {
fn new(
rs_name: String,
cpp_name: String,
by_value_safe: bool,
prelude_policy: PreludePolicy,
de_referencicate: bool,
is_ctype: bool,
is_cxx_container: bool,
extra_non_canonical_name: Option<String>,
) -> Self {
TypeDetails {
rs_name,
cpp_name,
by_value_safe,
prelude_policy,
de_referencicate,
is_ctype,
is_cxx_container,
extra_non_canonical_name,
}
}
fn get_prelude_entry(&self) -> Option<String> {
match self.prelude_policy {
PreludePolicy::Exclude => None,
PreludePolicy::IncludeNormal | PreludePolicy::IncludeTemplated => {
let tn = TypeName::new_from_user_input(&self.rs_name);
let cxx_name = tn.get_final_ident();
let (templating, payload) = match self.prelude_policy {
PreludePolicy::IncludeNormal => ("", "char* ptr"),
PreludePolicy::IncludeTemplated => ("template<typename T> ", "T* ptr"),
_ => unreachable!(),
};
Some(format!(
indoc! {"
/**
* <div rustbindgen=\"true\" replaces=\"{}\">
*/
{}class {} {{
{};
}};
"},
self.cpp_name, templating, cxx_name, payload
))
}
}
}
fn to_type_path(&self) -> TypePath {
let segs = self.rs_name.split("::").map(make_ident);
parse_quote! {
#(#segs)::*
}
}
}
pub(crate) struct TypeDatabase {
by_rs_name: HashMap<TypeName, TypeDetails>,
canonical_names: HashMap<TypeName, TypeName>,
}
lazy_static! {
pub(crate) static ref KNOWN_TYPES: TypeDatabase = create_type_database();
}
impl TypeDatabase {
fn get(&self, ty: &TypeName) -> Option<&TypeDetails> {
let canonical_name = self.canonical_names.get(ty).unwrap_or(ty);
self.by_rs_name.get(canonical_name)
}
pub(crate) fn get_prelude(&self) -> String {
itertools::join(
self.by_rs_name
.values()
.filter_map(|t| t.get_prelude_entry()),
"\n",
)
}
pub(crate) fn get_pod_safe_types<'a>(
&'a self,
) -> impl Iterator<Item = (&'a TypeName, bool)> + 'a {
self.by_rs_name
.iter()
.map(|(tn, td)| (tn, td.by_value_safe))
}
pub(crate) fn should_dereference_in_cpp(&self, typ: &TypePath) -> bool {
let tn = TypeName::from_type_path(typ);
let td = self.get(&tn);
if let Some(td) = td {
td.de_referencicate
} else {
false
}
}
pub(crate) fn known_type_substitute_path(&self, typ: &TypePath) -> Option<TypePath> {
let tn = TypeName::from_type_path(typ);
self.get(&tn).map(|td| td.to_type_path())
}
pub(crate) fn special_cpp_name(&self, rs: &TypeName) -> Option<String> {
self.get(rs).map(|x| x.cpp_name.to_string())
}
pub(crate) fn is_known_type(&self, ty: &TypeName) -> bool {
self.get(ty).is_some()
}
pub(crate) fn known_type_type_path(&self, ty: &TypeName) -> Option<TypePath> {
self.get(ty).map(|td| td.to_type_path())
}
pub(crate) fn is_ctype(&self, ty: &TypeName) -> bool {
self.get(ty).map(|td| td.is_ctype).unwrap_or(false)
}
pub(crate) fn is_cxx_acceptable_generic(&self, ty: &TypeName) -> bool {
self.get(ty).map(|x| x.is_cxx_container).unwrap_or(false)
}
}
fn create_type_database() -> TypeDatabase {
let mut by_rs_name = HashMap::new();
let mut do_insert =
|td: TypeDetails| by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);
do_insert(TypeDetails::new(
"cxx::UniquePtr".into(),
"std::unique_ptr".into(),
true,
PreludePolicy::IncludeTemplated,
false,
false,
true,
None,
));
do_insert(TypeDetails::new(
"cxx::CxxVector".into(),
"std::vector".into(),
false,
PreludePolicy::IncludeTemplated,
false,
false,
true,
None,
));
do_insert(TypeDetails::new(
"cxx::SharedPtr".into(),
"std::shared_ptr".into(),
true,
PreludePolicy::IncludeTemplated,
false,
false,
true,
None,
));
do_insert(TypeDetails::new(
"cxx::CxxString".into(),
"std::string".into(),
false,
PreludePolicy::IncludeNormal,
false,
false,
false,
None,
));
do_insert(TypeDetails::new(
"str".into(),
"rust::Str".into(),
true,
PreludePolicy::IncludeNormal,
true,
false,
false,
None,
));
do_insert(TypeDetails::new(
"String".into(),
"rust::String".into(),
true,
PreludePolicy::IncludeNormal,
false,
false,
false,
None,
));
for (cpp_type, rust_type) in (3..7)
.map(|x| 2i32.pow(x))
.map(|x| {
vec![
(format!("uint{}_t", x), format!("u{}", x)),
(format!("int{}_t", x), format!("i{}", x)),
]
})
.flatten()
{
do_insert(TypeDetails::new(
rust_type,
cpp_type,
true,
PreludePolicy::Exclude,
false,
false,
false,
None,
));
}
do_insert(TypeDetails::new(
"bool".into(),
"bool".into(),
true,
PreludePolicy::Exclude,
false,
false,
false,
None,
));
do_insert(TypeDetails::new(
"std::pin::Pin".into(),
"Pin".into(),
true, PreludePolicy::Exclude,
false,
false,
false,
None,
));
let mut insert_ctype = |cname: &str| {
let td = TypeDetails::new(
format!("autocxx::c_{}", cname),
cname.into(),
true,
PreludePolicy::Exclude,
false,
true,
false,
Some(format!("std::os::raw::c_{}", cname)),
);
by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);
let td = TypeDetails::new(
format!("autocxx::c_u{}", cname),
format!("unsigned {}", cname),
true,
PreludePolicy::Exclude,
false,
true,
false,
Some(format!("std::os::raw::c_u{}", cname)),
);
by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);
};
insert_ctype("long");
insert_ctype("int");
insert_ctype("short");
let td = TypeDetails::new(
"f32".into(),
"float".into(),
true,
PreludePolicy::Exclude,
false,
false,
false,
None,
);
by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);
let td = TypeDetails::new(
"f64".into(),
"double".into(),
true,
PreludePolicy::Exclude,
false,
false,
false,
None,
);
by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);
let td = TypeDetails::new(
"std::os::raw::c_char".into(),
"char".into(),
true,
PreludePolicy::Exclude,
false,
false,
false,
None,
);
by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);
let mut by_cppname = HashMap::new();
for td in by_rs_name.values() {
let rs_name = TypeName::new_from_user_input(&td.rs_name);
if let Some(extra_non_canonical_name) = &td.extra_non_canonical_name {
by_cppname.insert(
TypeName::new_from_user_input(extra_non_canonical_name),
rs_name.clone(),
);
}
by_cppname.insert(TypeName::new_from_user_input(&td.cpp_name), rs_name);
}
TypeDatabase {
by_rs_name,
canonical_names: by_cppname,
}
}
const BINDGEN_BLOCKLIST: &[&str] = &["std.*", "__gnu.*", ".*mbstate_t.*", "rust.*"];
pub(crate) fn get_initial_blocklist() -> Vec<String> {
BINDGEN_BLOCKLIST.iter().map(|s| s.to_string()).collect()
}
pub(crate) fn type_lacks_copy_constructor(ty: &Type) -> bool {
match ty {
Type::Path(typ) => {
let tn = TypeName::from_type_path(typ);
tn.to_cpp_name().starts_with("std::unique_ptr")
}
_ => false,
}
}