jan/browser/update-bindings/src/parse_tree.rs
2025-10-09 22:44:12 +07:00

3577 lines
142 KiB
Rust

use convert_case::{Case, Casing};
use quote::{format_ident, quote, ToTokens};
use regex::Regex;
use std::{
cell::OnceCell,
collections::BTreeMap,
fmt::{self, Debug, Display, Formatter},
fs,
io::Write,
iter::{self, Iterator},
path::{Path, PathBuf},
process::Command,
sync::OnceLock,
};
pub fn generate_bindings(source_path: &Path) -> crate::Result<PathBuf> {
let bindings = crate::read_bindings(source_path)?;
let parsed = syn::parse_file(&bindings)?;
let parse_tree = ParseTree::from(&parsed);
let mut out_file = crate::dirs::get_out_dir();
out_file.push("bindings.rs");
let mut bindings = fs::File::create(&out_file)?;
write!(bindings, "{parse_tree}")?;
format_bindings(&out_file)?;
Ok(out_file)
}
#[derive(Debug, Error)]
pub enum Unrecognized {
#[error("Unrecognized Field Type")]
FieldType,
#[error("Unrecognized Function Argument")]
FnArg,
#[error("Unrecognized Generic Type")]
Generic,
#[error("Unrecognized Interface Declaration")]
Interface,
#[error("Failed to Parse Bindings")]
Parse(#[from] syn::Error),
}
struct TypeAliasRef<'a> {
name: String,
ty: &'a syn::Type,
}
struct EnumRef<'a> {
name: String,
ty: Option<&'a syn::ItemEnum>,
}
struct FieldRef<'a> {
name: String,
ty: &'a syn::Type,
}
impl<'a> TryFrom<&'a syn::Field> for FieldRef<'a> {
type Error = Unrecognized;
fn try_from(value: &'a syn::Field) -> Result<Self, Self::Error> {
let name = value
.ident
.as_ref()
.ok_or(Unrecognized::FieldType)?
.to_string();
Ok(Self {
name,
ty: &value.ty,
})
}
}
#[derive(Clone)]
struct FnArgRef<'a> {
name: String,
ty: &'a syn::Type,
}
struct SignatureRef<'a> {
name: String,
inputs: Vec<FnArgRef<'a>>,
output: Option<&'a syn::Type>,
merged_params: OnceCell<Vec<MergedParam>>,
}
enum MergedParam {
Receiver,
Single {
name: String,
ty: Option<ModifiedType>,
},
Bounded {
count_name: String,
count_ty: ModifiedType,
slice_name: String,
slice_ty: ModifiedType,
},
Buffer {
slice_name: String,
slice_ty: ModifiedType,
size_name: String,
size_ty: ModifiedType,
},
}
impl SignatureRef<'_> {
fn capture_params(&self) -> Option<proc_macro2::TokenStream> {
let arg_names = self
.inputs
.iter()
.map(|arg| {
let name = make_snake_case_value_name(arg.name.as_str());
let local = format!("arg_{name}");
(local, name)
})
.collect::<Vec<_>>();
let local = arg_names
.iter()
.map(|(name, _)| format_ident!("{name}").to_token_stream());
let name = arg_names
.iter()
.map(|(_, name)| format_ident!("{name}").to_token_stream());
match arg_names.len() {
0 => None,
1 => Some(quote! { let #(#local)* = #(#name)*; }),
_ => Some(quote! { let (#(#local),*) = (#(#name),*); }),
}
}
fn merge_params(&self, tree: &ParseTree) -> impl Iterator<Item = &MergedParam> {
self.merged_params
.get_or_init(|| {
let mut args = self
.inputs
.iter()
.map(|arg| {
Some(MergedParam::Single {
name: make_snake_case_value_name(&arg.name),
ty: tree.resolve_modified_type(arg.ty),
})
})
.collect::<Vec<_>>();
for i in 1..(args.len()) {
let replacement = match (&args[i - 1], &args[i]) {
(
Some(MergedParam::Single {
name: count_name,
ty: Some(count_ty),
}),
Some(MergedParam::Single {
name: elem_name,
ty: Some(elem_ty),
}),
) if count_name.as_str() == format!("{elem_name}_count").as_str() => {
if count_ty.ty.to_token_stream().to_string().as_str()
!= format_ident!("usize")
.to_token_stream()
.to_string()
.as_str()
{
continue;
}
let elem_tokens = elem_ty.ty.to_token_stream();
let elem_ty_string = elem_tokens.to_string();
match tree.cef_name_map.get(&elem_ty_string) {
Some(NameMapEntry {
ty: NameMapType::StructDeclaration(_),
..
}) => {
let modifiers = match (
count_ty.modifiers.as_slice(),
elem_ty.modifiers.as_slice(),
) {
([], [TypeModifier::ConstPtr, TypeModifier::MutPtr]) => {
vec![TypeModifier::Slice]
}
(
[TypeModifier::MutPtr],
[TypeModifier::MutPtr, TypeModifier::MutPtr],
) => vec![TypeModifier::MutSlice],
_ => continue,
};
let Ok(slice_ty) = syn::parse2::<syn::Type>(elem_tokens) else {
continue;
};
Some(MergedParam::Bounded {
count_name: count_name.clone(),
count_ty: count_ty.clone(),
slice_name: elem_name.clone(),
slice_ty: ModifiedType {
modifiers,
ty: slice_ty,
},
})
}
_ => {
let modifiers = match (
count_ty.modifiers.as_slice(),
elem_ty.modifiers.as_slice(),
) {
([], [TypeModifier::ConstPtr]) => {
vec![TypeModifier::Slice]
}
([TypeModifier::MutPtr], [TypeModifier::MutPtr]) => {
vec![TypeModifier::MutSlice]
}
_ => continue,
};
let Ok(slice_ty) = syn::parse2::<syn::Type>(elem_tokens) else {
continue;
};
Some(MergedParam::Bounded {
count_name: count_name.clone(),
count_ty: count_ty.clone(),
slice_name: elem_name.clone(),
slice_ty: ModifiedType {
modifiers,
ty: slice_ty,
},
})
}
}
}
(
Some(MergedParam::Single {
name: elem_name,
ty: Some(elem_ty),
}),
Some(MergedParam::Single {
name: size_name,
ty: Some(size_ty),
}),
) if elem_ty.ty.to_token_stream().to_string()
== quote! { ::std::os::raw::c_void }.to_string() =>
{
if size_name.as_str() != format!("{elem_name}_size").as_str()
|| size_ty.ty.to_token_stream().to_string().as_str()
!= format_ident!("usize")
.to_token_stream()
.to_string()
.as_str()
{
continue;
}
let modifiers = match (
size_ty.modifiers.as_slice(),
elem_ty.modifiers.as_slice(),
) {
([], [TypeModifier::ConstPtr]) => {
vec![TypeModifier::Slice]
}
([], [TypeModifier::MutPtr])
| (
[TypeModifier::MutPtr],
[TypeModifier::MutPtr, TypeModifier::MutPtr],
) => vec![TypeModifier::MutSlice],
_ => continue,
};
// Remove the size argument and replace the buffer pointer argument with a
// &[u8] or &mut &mut [u8] slice.
Some(MergedParam::Buffer {
slice_name: elem_name.clone(),
slice_ty: ModifiedType {
modifiers,
ty: elem_ty.ty.clone(),
},
size_name: size_name.clone(),
size_ty: size_ty.clone(),
})
}
(
Some(MergedParam::Single {
name: elem_name,
ty: Some(elem_ty),
}),
Some(MergedParam::Single {
name: size_name,
ty: Some(size_ty),
}),
) if (size_name.as_str() == format!("{elem_name}_size").as_str()
|| size_name.as_str() == format!("{elem_name}_len").as_str())
&& size_ty.ty.to_token_stream().to_string().as_str()
== format_ident!("usize")
.to_token_stream()
.to_string()
.as_str() =>
{
let modifiers = match (
size_ty.modifiers.as_slice(),
elem_ty.modifiers.as_slice(),
) {
([], [TypeModifier::ConstPtr]) => {
vec![TypeModifier::Slice]
}
([], [TypeModifier::MutPtr])
| (
[TypeModifier::MutPtr],
[TypeModifier::MutPtr, TypeModifier::MutPtr],
) => vec![TypeModifier::MutSlice],
_ => continue,
};
// Remove the size argument and replace the buffer pointer argument with a
// &[elem_ty] or &mut &mut [elem_ty] slice.
Some(MergedParam::Buffer {
slice_name: elem_name.clone(),
slice_ty: ModifiedType {
modifiers,
ty: elem_ty.ty.clone(),
},
size_name: size_name.clone(),
size_ty: size_ty.clone(),
})
}
_ => None,
};
if let Some(replacement) = replacement {
args[i - 1] = Some(replacement);
args[i] = None;
}
}
args.into_iter()
.flatten()
.map(|arg| match arg {
MergedParam::Single { name, ty } => {
if name.as_str() == "self_" {
MergedParam::Receiver
} else {
MergedParam::Single { name, ty }
}
}
_ => arg,
})
.collect()
})
.iter()
}
fn capture_merged_params(&self, tree: &ParseTree) -> Option<proc_macro2::TokenStream> {
let arg_names = self
.merge_params(tree)
.flat_map(|arg| match arg {
MergedParam::Receiver => None,
MergedParam::Single { name, .. } => Some(name.clone()),
MergedParam::Bounded { slice_name, .. } => Some(slice_name.clone()),
MergedParam::Buffer { slice_name, .. } => Some(slice_name.clone()),
})
.map(|name| {
let local = format!("arg_{name}");
(local, name)
})
.collect::<Vec<_>>();
let local = arg_names
.iter()
.map(|(name, _)| format_ident!("{name}").to_token_stream());
let name = arg_names
.iter()
.map(|(_, name)| format_ident!("{name}").to_token_stream());
match arg_names.len() {
0 => None,
1 => Some(quote! { let #(#local)* = #(#name)*; }),
_ => Some(quote! { let (#(#local),*) = (#(#name),*); }),
}
}
fn get_rust_args(&self, tree: &ParseTree) -> proc_macro2::TokenStream {
let args = self.merge_params(tree).filter_map(|arg| match arg {
MergedParam::Receiver => Some(quote! { &self }),
MergedParam::Single { name, ty: Some(ty) } => {
let name = format_ident!("{name}");
let ty = ty
.get_argument_type(tree)
.unwrap_or_else(|| ty.ty.to_token_stream());
Some(quote! { #name: #ty })
}
MergedParam::Bounded {
slice_name,
slice_ty,
..
} => {
let slice_name = format_ident!("{slice_name}");
let slice_ty = slice_ty
.get_argument_type(tree)
.unwrap_or_else(|| slice_ty.ty.to_token_stream());
Some(quote! { #slice_name: #slice_ty })
}
MergedParam::Buffer {
slice_name,
slice_ty,
..
} => {
let slice_name = format_ident!("{slice_name}");
let slice_ty = slice_ty
.get_argument_type(tree)
.unwrap_or_else(|| slice_ty.ty.to_token_stream());
Some(quote! { #slice_name: #slice_ty })
}
_ => None,
});
quote! { #(#args),* }
}
fn unwrap_rust_args(&self, tree: &ParseTree) -> proc_macro2::TokenStream {
let capture = self.capture_merged_params(tree);
let args = self.merge_params(tree).filter_map(|arg| match arg {
MergedParam::Receiver => Some(quote! {
let arg_self_ = self.into_raw();
}),
MergedParam::Single {
name,
ty: Some(arg_ty),
} => {
let arg_name = format_ident!("arg_{name}");
let out_name = format_ident!("out_{name}");
let (modifiers, arg_ty) = (arg_ty.modifiers.as_slice(), &arg_ty.ty);
let ty_tokens = arg_ty.to_token_stream();
let ty_string = ty_tokens.to_string();
let entry = tree.cef_name_map.get(&ty_string);
(tree.root(&ty_string) == BASE_REF_COUNTED)
.then(|| {
match modifiers {
[TypeModifier::MutPtr, TypeModifier::MutPtr] => {
Some(quote! {
let #out_name = #arg_name;
let mut ptr = std::ptr::null_mut();
let (#out_name, #arg_name) = #out_name
.map(|arg| {
if let Some(arg) = arg.as_mut() {
arg.add_ref();
ptr = arg.get_raw();
}
(Some(arg), std::ptr::from_mut(&mut ptr))
})
.unwrap_or((None, std::ptr::null_mut()));
})
}
_ => {
if ty_string.as_str() == BASE_REF_COUNTED {
Some(quote!{
let #out_name = #arg_name;
let #arg_name = #out_name.map(|arg| {
arg.add_ref();
arg.into_raw()
}).unwrap_or(std::ptr::null_mut());
})
} else {
let cast = entry.and_then(|entry| {
syn::parse_str::<syn::Type>(&entry.name).ok()
})
.map(|ty| {
let ty = ty.to_token_stream().to_string();
let ty = format_ident!("Impl{ty}");
quote!{ #ty::get_raw(arg) }
})
.unwrap_or_else(|| quote!{ arg.get_raw() });
Some(quote! {
let #arg_name = #arg_name.map(|arg| {
arg.add_ref();
#cast
}).unwrap_or(std::ptr::null_mut());
})
}
},
}
})
.flatten()
.or_else(|| {
match entry {
Some(NameMapEntry {
ty: NameMapType::StructDeclaration(_),
..
}) if tree.lookup_struct_declaration
.get(&ty_string)
.and_then(|i| tree.struct_declarations.get(*i))
.map(|s| s.methods.is_empty()
&& !s.fields.is_empty()
&& !s.fields.iter().map(|f| f.name.as_str()).eq(["_unused"]))
.unwrap_or_default() =>
{
match modifiers {
[TypeModifier::ConstPtr] => {
Some(quote! {
let #arg_name = #arg_name.cloned().map(|arg| arg.into());
let #arg_name = #arg_name.as_ref().map(std::ptr::from_ref).unwrap_or(std::ptr::null());;
})
}
[TypeModifier::MutPtr] => {
Some(quote! {
let mut #arg_name = #arg_name.cloned().map(|arg| arg.into());
let #arg_name = #arg_name.as_mut().map(std::ptr::from_mut).unwrap_or(std::ptr::null_mut());;
})
}
_ => None,
}
}
Some(NameMapEntry {
ty: NameMapType::StructDeclaration(_),
..
}) => {
let impl_default = match modifiers {
[TypeModifier::ConstPtr] => {
quote! { unwrap_or(std::ptr::null()) }
}
[TypeModifier::MutPtr] => {
quote! { unwrap_or(std::ptr::null_mut()) }
}
_ => quote! { unwrap_or_default() },
};
Some(quote! {
let #arg_name = #arg_name.map(|arg| arg.into_raw()).#impl_default;
})
}
Some(NameMapEntry {
ty: NameMapType::TypeAlias,
..
}) if ty_string.as_str() == "cef_string_t" => {
let impl_default = match modifiers {
[TypeModifier::ConstPtr] => {
quote! { unwrap_or(std::ptr::null()) }
}
[TypeModifier::MutPtr] => {
quote! { unwrap_or(std::ptr::null_mut()) }
}
_ => quote! { unwrap_or_default() },
};
Some(quote! {
let #arg_name = #arg_name.map(|arg| arg.into_raw()).#impl_default;
})
}
Some(_) => {
Some(quote! {
let #arg_name = #arg_name.into_raw();
})
}
None => {
let is_void = ty_string == quote!{ ::std::os::raw::c_void }.to_string();
let cast = match modifiers {
[TypeModifier::MutPtr] if !is_void => Some(quote! {
#arg_name
.map(std::ptr::from_mut)
.unwrap_or(std::ptr::null_mut())
}),
[TypeModifier::ConstPtr] if !is_void => Some(quote! {
#arg_name
.map(std::ptr::from_ref)
.unwrap_or(std::ptr::null())
}),
[TypeModifier::MutPtr | TypeModifier::ConstPtr, ..] => Some(quote! { #arg_name.cast() }),
_ => None,
};
cast.map(|cast| quote! {
let #arg_name = #cast;
})
}
}
})
}
MergedParam::Bounded {
count_name,
count_ty:
ModifiedType {
modifiers: count_modifiers,
..
},
slice_name,
slice_ty:
ModifiedType {
ty: slice_ty,
..
}
} => {
let out_count = format_ident!("out_{count_name}");
let arg_count = format_ident!("arg_{count_name}");
let arg_name = format_ident!("arg_{slice_name}");
let out_name = format_ident!("out_{slice_name}");
let vec_name = format_ident!("vec_{slice_name}");
let add_refs = if tree.root(&slice_ty.to_token_stream().to_string()) == BASE_REF_COUNTED {
Some(quote! { elem.add_ref(); })
} else {
None
};
match count_modifiers.as_slice() {
[] => Some(quote! {
let #arg_count = #arg_name
.as_ref()
.map(|arg| arg.len())
.unwrap_or_default();
let #vec_name = #arg_name
.as_ref()
.map(|arg| arg
.iter()
.map(|elem| elem
.as_ref()
.map(|elem| {
#add_refs
elem.get_raw()
})
.unwrap_or(std::ptr::null_mut()))
.collect::<Vec<_>>())
.unwrap_or_default();
let #arg_name = if #vec_name.is_empty() {
std::ptr::null()
} else {
#vec_name.as_ptr()
};
}),
[TypeModifier::MutPtr] => Some(quote! {
let mut #out_count = #arg_name
.as_ref()
.map(|arg| arg.len())
.unwrap_or_default();
let #arg_count = &mut #out_count;
let #out_name = #arg_name;
let mut #vec_name = #out_name
.as_ref()
.map(|arg| arg
.iter()
.map(|elem| elem
.as_ref()
.map(|elem| {
#add_refs
elem.get_raw()
})
.unwrap_or(std::ptr::null_mut()))
.collect::<Vec<_>>())
.unwrap_or_default();
let #arg_name = if #vec_name.is_empty() {
std::ptr::null_mut()
} else {
#vec_name.as_mut_ptr()
};
}),
_ => None,
}
}
MergedParam::Buffer {
slice_name,
slice_ty,
size_name,
size_ty:
ModifiedType {
modifiers: size_modifiers,
..
},
} => {
let out_name = format_ident!("out_{slice_name}");
let arg_name = format_ident!("arg_{slice_name}");
let out_size = format_ident!("out_{size_name}");
let arg_size = format_ident!("arg_{size_name}");
match slice_ty.modifiers.as_slice() {
[TypeModifier::Slice] => Some(quote! {
let #arg_size = #arg_name
.as_ref()
.map(|arg| arg.len())
.unwrap_or_default();
let #arg_name = #arg_name.and_then(|arg| {
if arg.is_empty() {
None
} else {
Some(arg.as_ptr().cast())
}
})
.unwrap_or(std::ptr::null());
}),
[TypeModifier::MutSlice] => {
let arg_size = match size_modifiers.as_slice() {
[] => Some(quote! {
let #arg_size = #arg_name
.as_ref()
.map(|arg| arg.len())
.unwrap_or_default();
}),
[TypeModifier::MutPtr] => Some(quote! {
let mut #out_size = #arg_name
.as_ref()
.map(|arg| arg.len())
.unwrap_or_default();
let #arg_size = &mut #out_size;
}),
_ => None,
};
Some(quote! {
#arg_size
let mut #out_name = #arg_name;
let #arg_name = #out_name.as_mut().and_then(|arg| {
if arg.is_empty() {
None
} else {
Some(arg.as_mut_ptr().cast())
}
})
.unwrap_or(std::ptr::null_mut());
})
}
_ => None,
}
}
_ => None,
});
quote! {
#capture
#(#args)*
}
}
fn rewrap_rust_args(&self, tree: &ParseTree) -> proc_macro2::TokenStream {
let args = self.merge_params(tree).filter_map(|arg| match arg {
MergedParam::Single {
name,
ty: Some(arg_ty),
} => {
let (modifiers, arg_ty) = (arg_ty.modifiers.as_slice(), &arg_ty.ty);
let ty_tokens = arg_ty.to_token_stream();
let ty_string = ty_tokens.to_string();
match modifiers {
[TypeModifier::MutPtr, TypeModifier::MutPtr] if tree.root(&ty_string) == BASE_REF_COUNTED => {
let arg_name = format_ident!("arg_{name}");
let out_name = format_ident!("out_{name}");
Some(quote! {
if let (Some(#out_name), Some(#arg_name)) = (#out_name, #arg_name.as_ref()) {
*#out_name = #arg_name.as_mut().map(|arg| std::ptr::from_mut(arg).wrap_result());
}
})
}
_ => None,
}
}
MergedParam::Bounded {
count_name,
count_ty:
ModifiedType {
modifiers: count_modifiers,
..
},
slice_name,
..
} if matches!(count_modifiers.as_slice(), [TypeModifier::MutPtr]) => {
let out_count = format_ident!("out_{count_name}");
let out_name = format_ident!("out_{slice_name}");
let vec_name = format_ident!("vec_{slice_name}");
Some(quote! {
if let Some(#out_name) = #out_name {
*#out_name = #vec_name
.into_iter()
.take(#out_count)
.map(|elem| if elem.is_null() { None } else { Some(elem.wrap_result()) })
.collect();
}
})
}
MergedParam::Buffer {
slice_name,
slice_ty,
size_name,
size_ty:
ModifiedType {
modifiers: size_modifiers,
..
},
} if matches!(
(slice_ty.modifiers.as_slice(), size_modifiers.as_slice()),
([TypeModifier::MutSlice], [TypeModifier::MutPtr])
) =>
{
let out_name = format_ident!("out_{slice_name}");
let out_size = format_ident!("out_{size_name}");
Some(quote! {
if let Some(#out_name) = #out_name {
#out_name.resize(#out_size, Default::default());
}
})
}
_ => None,
});
quote! { #(#args)* }
}
fn get_rust_output(&self, tree: &ParseTree) -> Option<proc_macro2::TokenStream> {
self.output.map(|output| {
let ty = tree
.resolve_modified_type(output)
.and_then(|ty| ty.get_output_type(tree))
.unwrap_or_else(|| {
let output = output.to_token_stream();
quote! { #output }
});
quote! { -> #ty }
})
}
fn wrap_cef_args(&self, tree: &ParseTree) -> proc_macro2::TokenStream {
let capture = self.capture_params();
let args = self.merge_params(tree).filter_map(|arg| match arg {
MergedParam::Receiver => Some(quote! {
let arg_self_: &RcImpl<_, I> = RcImpl::get(arg_self_);
}),
MergedParam::Single {
name,
ty: Some(arg_ty),
} => {
let arg_name = format_ident!("arg_{name}");
let out_name = format_ident!("out_{name}");
let wrap_name = format_ident!("wrap_{name}");
let (modifiers, arg_ty) = (arg_ty.modifiers.as_slice(), &arg_ty.ty);
let ty_tokens = arg_ty.to_token_stream();
let ty_string = ty_tokens.to_string();
let entry = tree.cef_name_map.get(ty_string.as_str());
let root = tree.root(&ty_string);
(root == BASE_REF_COUNTED)
.then(|| {
match entry? {
NameMapEntry {
name,
ty: NameMapType::StructDeclaration(_),
} => {
let name = format_ident!("{name}");
match modifiers {
[TypeModifier::ConstPtr] => Some(quote! {
let #arg_name = unsafe { #arg_name.as_ref() }.map(|arg| {
#name(unsafe { RefGuard::from_raw(arg) })
});
let #arg_name = #arg_name.as_ref();
}),
[TypeModifier::MutPtr] => Some(quote! {
let mut #arg_name = unsafe { #arg_name.as_mut() }.map(|arg| {
#name(unsafe { RefGuard::from_raw(arg) })
});
let #arg_name = #arg_name.as_mut();
}),
[TypeModifier::MutPtr, TypeModifier::MutPtr] => Some(quote! {
let #out_name = #arg_name;
let mut #wrap_name = unsafe { #arg_name.as_mut() }.and_then(|ptr| {
if ptr.is_null() {
None
} else {
Some(#name(unsafe { RefGuard::from_raw(*ptr) }))
}
});
let #arg_name = Some(&mut #wrap_name);
}),
_ => None,
}
}
_ => None,
}
})
.flatten()
.or_else(|| {
if root == BASE_SCOPED {
let Some(NameMapEntry { name, ty: NameMapType::StructDeclaration(_) }) = entry else {
return None;
};
let name = format_ident!("{name}");
match modifiers {
[TypeModifier::MutPtr] => Some(quote! {
let mut #arg_name = if #arg_name.is_null() { None } else { Some(#name(#arg_name)) };
let #arg_name = #arg_name.as_mut();
}),
[TypeModifier::ConstPtr] => Some(quote! {
let #arg_name = if #arg_name.is_null() { None } else { Some(#name(#arg_name)) };
let #arg_name = #arg_name.as_ref();
}),
_ => None,
}
} else if ty_string.as_str() == "cef_string_t" ||
CUSTOM_STRING_TYPES.contains(&ty_string.as_str())
{
match modifiers {
[TypeModifier::MutPtr] => Some(quote! {
let mut #arg_name = if #arg_name.is_null() { None } else { Some(#arg_name.into()) };
let #arg_name = #arg_name.as_mut();
}),
[TypeModifier::ConstPtr] => Some(quote! {
let #arg_name = if #arg_name.is_null() { None } else { Some(#arg_name.into()) };
let #arg_name = #arg_name.as_ref();
}),
_ => None,
}
} else {
let ty = entry.and_then(|entry| syn::parse_str::<syn::Type>(&entry.name).ok());
let ty = ty.as_ref().unwrap_or(arg_ty).to_token_stream();
if ty.to_string() == quote!{ ::std::os::raw::c_void }.to_string() {
match modifiers {
[TypeModifier::MutPtr] => Some(quote! {
let #arg_name = #arg_name.cast();
}),
[TypeModifier::ConstPtr] => Some(quote! {
let #arg_name = #arg_name.cast();
}),
_ => {
Some(quote! {})
}
}
} else {
match modifiers {
[TypeModifier::MutPtr] => Some(quote! {
let mut #arg_name = if #arg_name.is_null() {
None
} else {
Some(WrapParamRef::<#ty, _>::from(#arg_name))
};
let #arg_name = #arg_name.as_mut().map(|arg| arg.as_mut());
}),
[TypeModifier::ConstPtr] => Some(quote! {
let #arg_name = if #arg_name.is_null() {
None
} else {
Some(WrapParamRef::<#ty, _>::from(#arg_name))
};
let #arg_name = #arg_name.as_ref().map(|arg| arg.as_ref());
}),
_ => None,
}
}
}
})
.or(Some(quote! { let #arg_name = #arg_name.into_raw(); }))
}
MergedParam::Bounded {
count_name,
slice_name,
slice_ty,
..
} => {
let out_count = format_ident!("out_{count_name}");
let arg_count = format_ident!("arg_{count_name}");
let out_name = format_ident!("out_{slice_name}");
let arg_name = format_ident!("arg_{slice_name}");
let vec_name = format_ident!("vec_{slice_name}");
let (modifiers, slice_ty) = (slice_ty.modifiers.as_slice(), &slice_ty.ty);
let ty_tokens = slice_ty.to_token_stream();
let ty_string = ty_tokens.to_string();
let entry = tree.cef_name_map.get(ty_string.as_str());
(tree.root(&ty_string) == BASE_REF_COUNTED)
.then(|| {
match entry? {
NameMapEntry {
name,
ty: NameMapType::StructDeclaration(_),
} => {
let name = format_ident!("{name}");
match modifiers {
[TypeModifier::Slice] => {
Some(quote! {
let #vec_name = unsafe { #arg_name.as_ref() }.map(|arg| {
let arg = unsafe { std::slice::from_raw_parts(std::ptr::from_ref(arg), #arg_count) };
arg.iter()
.map(|arg| {
if arg.is_null() {
None
} else {
Some(#name(unsafe { RefGuard::from_raw(*arg) }))
}
})
.collect::<Vec<_>>()
});
let #arg_name = #vec_name.as_deref();
})
},
[TypeModifier::MutSlice] => {
Some(quote! {
let #out_count = unsafe { #arg_count.as_mut() };
let #out_name = unsafe { #arg_name.as_mut() };
let #arg_count = #out_count
.as_ref()
.map(|count| **count)
.unwrap_or_default();
let mut #vec_name = unsafe { #arg_name.as_mut() }.map(|arg| {
let arg = unsafe { std::slice::from_raw_parts_mut(std::ptr::from_mut(arg), #arg_count) };
arg.iter_mut()
.map(|arg| {
if arg.is_null() {
None
} else {
Some(#name(unsafe { RefGuard::from_raw(*arg) }))
}
})
.collect::<Vec<_>>()
});
let #arg_name = #vec_name.as_mut();
})
},
_ => None,
}
}
_ => None,
}
})
.flatten()
.or_else(|| {
if ty_string.as_str() == "cef_string_t" ||
CUSTOM_STRING_TYPES.contains(&ty_string.as_str())
{
None
} else {
let ty =
entry.and_then(|entry| syn::parse_str::<syn::Type>(&entry.name).ok());
let ty = ty.as_ref().unwrap_or(slice_ty).to_token_stream();
match modifiers {
[TypeModifier::MutPtr, ..] => Some(quote! {
let mut #arg_name = WrapParamRef::<#ty, _>::from(#arg_name);
let #arg_name = #arg_name.as_mut();
}),
[TypeModifier::ConstPtr, ..] => Some(quote! {
let #arg_name = WrapParamRef::<#ty, _>::from(#arg_name);
let #arg_name = #arg_name.as_ref();
}),
_ => None,
}
}
})
.or(Some(quote! { let #arg_name = #arg_name.into_raw(); }))
}
MergedParam::Buffer {
slice_name,
slice_ty: ModifiedType { modifiers: slice_modifiers, .. },
size_name,
size_ty:
ModifiedType {
modifiers: size_modifiers,
..
},
} => {
let out_name = format_ident!("out_{slice_name}");
let arg_name = format_ident!("arg_{slice_name}");
let vec_name = format_ident!("vec_{slice_name}");
let out_size = format_ident!("out_{size_name}");
let arg_size = format_ident!("arg_{size_name}");
match slice_modifiers.as_slice() {
[TypeModifier::Slice] => Some(quote! {
let #arg_name = (!#arg_name.is_null() && #arg_size > 0).then(|| unsafe {
std::slice::from_raw_parts(#arg_name.cast(), #arg_size)
});
}),
[TypeModifier::MutSlice] => {
let out_size = match size_modifiers.as_slice() {
[TypeModifier::MutPtr] => Some(quote! {
let #out_size = unsafe { #arg_size.as_mut() };
let #arg_size = #out_size.as_ref().map(|size| **size).unwrap_or_default();
}),
_ => None,
};
Some(quote! {
#out_size
let #out_name = (!#arg_name.is_null() && #arg_size > 0).then(|| unsafe {
std::slice::from_raw_parts_mut(#arg_name.cast(), #arg_size)
});
let mut #vec_name = #out_name.as_ref().map(|arg| arg.to_vec());
let #arg_name = #vec_name.as_mut();
})
}
_ => None,
}
.or(Some(quote! { let #arg_name = #arg_name.into_raw(); }))
}
_ => None,
});
quote! {
#capture
#(#args)*
}
}
fn unwrap_cef_args(&self, tree: &ParseTree) -> proc_macro2::TokenStream {
let args = self.merge_params(tree).filter_map(|arg| match arg {
MergedParam::Single {
name,
ty: Some(arg_ty),
} => {
let (modifiers, arg_ty) = (arg_ty.modifiers.as_slice(), &arg_ty.ty);
let ty_tokens = arg_ty.to_token_stream();
let ty_string = ty_tokens.to_string();
match modifiers {
[TypeModifier::MutPtr, TypeModifier::MutPtr] if tree.root(&ty_string) == BASE_REF_COUNTED => {
let out_name = format_ident!("out_{name}");
let wrap_name = format_ident!("wrap_{name}");
Some(quote! {
if let (Some(#out_name), Some(#wrap_name)) = (unsafe { #out_name.as_mut() }, #wrap_name) {
*#out_name = #wrap_name.wrap_result();
}
})
}
_ => None,
}
}
MergedParam::Bounded {
count_name,
count_ty:
ModifiedType {
modifiers: count_modifiers,
..
},
slice_name,
slice_ty: ModifiedType {
ty: slice_ty,
modifiers: slice_modifiers,
},
} => {
let out_count = format_ident!("out_{count_name}");
let vec_name = format_ident!("vec_{slice_name}");
let update_count = match count_modifiers.as_slice() {
[TypeModifier::MutPtr] => {
Some(quote! {
*#out_count = size;
})
}
_ => None,
};
let add_refs = match (slice_modifiers.as_slice(), tree.root(&slice_ty.to_token_stream().to_string())) {
([TypeModifier::MutSlice], BASE_REF_COUNTED) => {
Some(quote! {
for elem in &mut #vec_name[..size] {
if let Some(elem) = elem.as_ref() {
unsafe { elem.add_ref() };
}
}
})
}
_ => None,
};
match (add_refs, update_count) {
(None, None) => None,
(add_refs, update_count) => {
Some(quote! {
if let (Some(#out_count), Some(#vec_name)) = (#out_count, #vec_name.as_mut()) {
let size = #vec_name.len().min(*#out_count);
#add_refs;
#update_count;
}
})
},
}
}
MergedParam::Buffer {
slice_name,
slice_ty,
size_name,
size_ty:
ModifiedType {
modifiers: size_modifiers,
..
},
} => {
let out_name = format_ident!("out_{slice_name}");
let vec_name = format_ident!("vec_{slice_name}");
let out_size = format_ident!("out_{size_name}");
match (slice_ty.modifiers.as_slice(), size_modifiers.as_slice()) {
([TypeModifier::MutSlice], [TypeModifier::MutPtr]) => {
Some(quote! {
if let (Some(#out_size), Some(#out_name), Some(#vec_name)) = (#out_size, #out_name, #vec_name.as_mut()) {
*#out_size = #vec_name.len().min(*#out_size);
#out_name[..(*#out_size)].copy_from_slice(&#vec_name[..(*#out_size)]);
}
})
}
([TypeModifier::MutSlice], []) => {
Some(quote! {
if let (Some(#out_name), Some(#vec_name)) = (#out_name, #vec_name.as_mut()) {
let size = #vec_name.len().min(#out_name.len());
#out_name[..size].copy_from_slice(&#vec_name[..size]);
}
})
}
_ => None,
}
}
_ => None,
});
quote! { #(#args)* }
}
fn get_signature(&self, tree: &ParseTree) -> proc_macro2::TokenStream {
let name = make_rust_method_name(&self.name);
let name = format_ident!("{name}");
let args = self.get_rust_args(tree);
let output = self.get_rust_output(tree);
quote! { fn #name(#args) #output }
}
}
impl<'a> TryFrom<&'a syn::Field> for SignatureRef<'a> {
type Error = Unrecognized;
fn try_from(value: &'a syn::Field) -> Result<Self, Self::Error> {
let name = value
.ident
.as_ref()
.ok_or(Unrecognized::FieldType)?
.to_string();
// Look for a type matching std::option::Option<T>
let syn::Type::Path(syn::TypePath {
qself: None,
path: syn::Path { segments, .. },
}) = &value.ty
else {
return Err(Unrecognized::FieldType);
};
let mut segments_iter = segments.iter();
let (
Some(syn::PathSegment {
ident: ident_std,
arguments: syn::PathArguments::None,
}),
Some(syn::PathSegment {
ident: ident_option,
arguments: syn::PathArguments::None,
}),
Some(syn::PathSegment {
ident: ident_type,
arguments:
syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
args, ..
}),
}),
None,
) = (
segments_iter.next(),
segments_iter.next(),
segments_iter.next(),
segments_iter.next(),
)
else {
return Err(Unrecognized::FieldType);
};
if *ident_std != "std"
|| *ident_option != "option"
|| *ident_type != "Option"
|| args.len() != 1
{
return Err(Unrecognized::FieldType);
}
// See if the Option<T> type is a function pointer
let mut args = args.iter();
let (
Some(syn::GenericArgument::Type(syn::Type::BareFn(syn::TypeBareFn {
unsafety: Some(_),
abi: Some(syn::Abi {
name: Some(abi), ..
}),
inputs,
variadic: None,
output,
..
}))),
None,
) = (args.next(), args.next())
else {
return Err(Unrecognized::FieldType);
};
if abi.value() != "C" {
return Err(Unrecognized::FieldType);
}
let inputs = inputs
.iter()
.filter_map(|arg| {
arg.name.as_ref().map(|(ident, _)| FnArgRef {
name: ident.to_string(),
ty: &arg.ty,
})
})
.collect();
let output = match output {
syn::ReturnType::Default => None,
syn::ReturnType::Type(_, ty) => Some(ty.as_ref()),
};
Ok(Self {
name,
inputs,
output,
merged_params: Default::default(),
})
}
}
const BASE_REF_COUNTED: &str = "_cef_base_ref_counted_t";
const BASE_SCOPED: &str = "_cef_base_scoped_t";
const CUSTOM_STRING_TYPES: &[&str] = &[
"_cef_string_utf8_t",
"_cef_string_utf16_t",
"_cef_string_wide_t",
"_cef_string_list_t",
"_cef_string_map_t",
"_cef_string_multimap_t",
];
const CUSTOM_STRING_USERFREE_ALIASES: &[&str] = &[
"cef_string_userfree_utf8_t",
"cef_string_userfree_utf16_t",
"cef_string_userfree_wide_t",
];
fn is_custom_string_userfree_alias(name: &str) -> bool {
name == "cef_string_t"
|| name == "cef_string_userfree_t"
|| CUSTOM_STRING_USERFREE_ALIASES.contains(&name)
}
struct StructDeclarationRef<'a> {
name: String,
fields: Vec<FieldRef<'a>>,
methods: Vec<SignatureRef<'a>>,
is_sealed: bool,
}
#[derive(Clone, Copy, Debug)]
enum NameMapType {
TypeAlias,
EnumName,
StructDeclaration(bool /* is_sealed */),
}
struct NameMapEntry {
name: String,
ty: NameMapType,
}
#[derive(Clone, Debug)]
enum TypeModifier {
MutPtr,
ConstPtr,
MutSlice,
MutRef,
Slice,
Ref,
Array { size: proc_macro2::TokenStream },
}
#[derive(Clone)]
struct ModifiedType {
modifiers: Vec<TypeModifier>,
ty: syn::Type,
}
impl ModifiedType {
fn get_argument_type(&self, tree: &ParseTree) -> Option<proc_macro2::TokenStream> {
let elem = self.ty.to_token_stream();
let elem_string = elem.to_string();
match tree.cef_name_map.get(&elem_string) {
Some(NameMapEntry {
name,
ty: NameMapType::StructDeclaration(is_sealed),
}) => {
let is_sealed = *is_sealed;
let root = tree.root(&elem_string);
if BASE_REF_COUNTED == root && root != elem_string.as_str() {
let impl_trait = format_ident!("Impl{name}");
let name = format_ident!("{name}");
match self.modifiers.as_slice() {
[TypeModifier::ConstPtr] => Some(if is_sealed {
quote! { Option<&#name> }
} else {
quote! { Option<&impl #impl_trait> }
}),
[TypeModifier::MutPtr] => Some(quote! { Option<&mut #name> }),
[TypeModifier::MutPtr, TypeModifier::MutPtr] => {
Some(quote! { Option<&mut Option<#name>> })
}
[TypeModifier::Slice] => Some(if is_sealed {
quote! { Option<&[Option<#name>]> }
} else {
quote! { Option<&[Option<impl #impl_trait>]> }
}),
[TypeModifier::MutSlice] => {
Some(quote! { Option<&mut Vec<Option<#name>>> })
}
_ => None,
}
} else {
let name = format_ident!("{name}");
match self.modifiers.as_slice() {
[TypeModifier::ConstPtr] => Some(quote! { Option<&#name> }),
[TypeModifier::MutPtr] => Some(quote! { Option<&mut #name> }),
[TypeModifier::MutPtr, TypeModifier::MutPtr] => {
Some(quote! { Option<&mut Option<#name>> })
}
[TypeModifier::Slice] => Some(quote! { Option<&[Option<#name>]> }),
[TypeModifier::MutSlice] => {
Some(quote! { Option<&mut Vec<Option<#name>>> })
}
_ => None,
}
}
}
Some(NameMapEntry {
name,
ty: NameMapType::TypeAlias,
}) if is_custom_string_userfree_alias(elem_string.as_str()) => {
let name = format_ident!("{name}");
match self.modifiers.as_slice() {
[] => Some(quote! { #name }),
[TypeModifier::ConstPtr] => Some(quote! { Option<&#name> }),
[TypeModifier::MutPtr] => Some(quote! { Option<&mut #name> }),
[TypeModifier::MutPtr, TypeModifier::MutPtr] => {
Some(quote! { Option<&mut Option<#name>> })
}
[TypeModifier::Slice] => Some(quote! { Option<&[Option<#name>]> }),
[TypeModifier::MutSlice] => Some(quote! { Option<&mut Vec<Option<#name>>> }),
_ => None,
}
}
Some(NameMapEntry {
name,
ty: NameMapType::EnumName,
}) => {
let name = format_ident!("{name}");
match self.modifiers.as_slice() {
[] => Some(quote! { #name }),
[TypeModifier::MutPtr] => Some(quote! { Option<&mut #name> }),
[TypeModifier::Slice] => Some(quote! { Option<&[#name]> }),
[TypeModifier::MutSlice] => Some(quote! { Option<&mut Vec<#name>> }),
_ => None,
}
}
None => {
let is_void = elem_string == quote! { ::std::os::raw::c_void }.to_string();
let elem = if is_void {
quote! { u8 }
} else {
elem
};
match self.modifiers.as_slice() {
[TypeModifier::ConstPtr] if !is_void => Some(quote! { Option<&#elem> }),
[TypeModifier::MutPtr] if !is_void => Some(quote! { Option<&mut #elem> }),
[TypeModifier::Slice] => Some(quote! { Option<&[#elem]> }),
[TypeModifier::MutSlice] => Some(quote! { Option<&mut Vec<#elem>> }),
modifiers => {
let modifiers = modifiers
.iter()
.map(|modifier| match modifier {
TypeModifier::MutPtr => Some(quote! { *mut }),
TypeModifier::ConstPtr => Some(quote! { *const }),
_ => None,
})
.collect::<Vec<_>>();
if modifiers.iter().any(Option::is_none) {
None
} else {
Some(quote! { #(#modifiers)* #elem})
}
}
}
}
_ => {
let modifiers = self
.modifiers
.iter()
.map(|modifier| match modifier {
TypeModifier::MutPtr => Some(quote! { *mut }),
TypeModifier::ConstPtr => Some(quote! { *const }),
_ => None,
})
.collect::<Vec<_>>();
if modifiers.iter().any(Option::is_none) {
None
} else {
Some(quote! { #(#modifiers)* #elem})
}
}
}
}
fn get_output_type(&self, tree: &ParseTree) -> Option<proc_macro2::TokenStream> {
let elem = self.ty.to_token_stream();
let elem_name = elem.to_string();
tree.cef_name_map
.get(&elem_name)
.and_then(|entry| match entry {
NameMapEntry {
name,
ty: NameMapType::StructDeclaration(_),
} => {
let name = format_ident!("{name}");
match self.modifiers.as_slice() {
[] => Some(quote! { #name }),
[TypeModifier::MutPtr] => Some(quote! { Option<#name> }),
[TypeModifier::ConstPtr] => Some(quote! { Option<&#name> }),
[TypeModifier::ConstPtr, TypeModifier::MutPtr] => {
Some(quote! { Option<&mut [#name>]> })
}
[TypeModifier::ConstPtr, TypeModifier::ConstPtr] => {
Some(quote! { Option<&[#name]> })
}
_ => None,
}
}
NameMapEntry {
name,
ty: NameMapType::TypeAlias,
} if is_custom_string_userfree_alias(elem_name.as_str()) => {
let name = format_ident!("{name}");
match self.modifiers.as_slice() {
[] => Some(quote! { #name }),
[TypeModifier::MutPtr] => Some(quote! { Option<#name> }),
[TypeModifier::ConstPtr] => Some(quote! { Option<&#name> }),
[TypeModifier::ConstPtr, TypeModifier::MutPtr] => {
Some(quote! { Option<&mut [#name>]> })
}
[TypeModifier::ConstPtr, TypeModifier::ConstPtr] => {
Some(quote! { Option<&[#name]> })
}
_ => None,
}
}
NameMapEntry {
name,
ty: NameMapType::EnumName,
} => {
let name = format_ident!("{name}");
match self.modifiers.as_slice() {
[] => Some(quote! { #name }),
[TypeModifier::ConstPtr] => Some(quote! { Option<&[#name]> }),
[TypeModifier::MutPtr] => Some(quote! { Option<&mut [#name]> }),
_ => None,
}
}
_ => match self.modifiers.as_slice() {
[TypeModifier::MutPtr] => Some(quote! { Option<&mut #elem> }),
[TypeModifier::ConstPtr] => Some(quote! { Option<&[#elem]> }),
[TypeModifier::MutPtr, TypeModifier::MutPtr] => {
Some(quote! { Option<&mut Vec<#elem>> })
}
_ => None,
},
})
}
}
impl syn::parse::Parse for ModifiedType {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut modifiers = vec![];
loop {
let lookahead = input.lookahead1();
if lookahead.peek(syn::Token![*]) {
let _ = input.parse::<syn::Token![*]>()?;
let lookahead = input.lookahead1();
if lookahead.peek(syn::Token![const]) {
let _ = input.parse::<syn::Token![const]>()?;
modifiers.push(TypeModifier::ConstPtr)
} else {
let _ = input.parse::<syn::Token![mut]>()?;
modifiers.push(TypeModifier::MutPtr)
}
} else if lookahead.peek(syn::Token![&]) {
let _ = input.parse::<syn::Token![&]>()?;
let lookahead = input.lookahead1();
if lookahead.peek(syn::Token![mut]) {
let _ = input.parse::<syn::Token![mut]>()?;
let lookahead = input.lookahead1();
if lookahead.peek(syn::token::Bracket) {
let ty;
let _ = syn::bracketed!(ty in input);
let ty = ty.parse()?;
modifiers.push(TypeModifier::MutSlice);
return Ok(Self { modifiers, ty });
} else {
modifiers.push(TypeModifier::MutRef)
}
} else if lookahead.peek(syn::token::Bracket) {
let ty;
let _ = syn::bracketed!(ty in input);
let ty = ty.parse()?;
modifiers.push(TypeModifier::Slice);
return Ok(Self { modifiers, ty });
} else {
modifiers.push(TypeModifier::Ref)
}
} else if lookahead.peek(syn::token::Bracket) {
let content;
let _ = syn::bracketed!(content in input);
let ty = content.parse()?;
let _ = content.parse::<syn::Token![;]>()?;
modifiers.push(TypeModifier::Array {
size: content.parse()?,
});
return Ok(Self { modifiers, ty });
} else {
break;
}
}
let ty = input.parse()?;
Ok(Self { modifiers, ty })
}
}
#[derive(Default)]
struct ParseTree<'a> {
type_aliases: Vec<TypeAliasRef<'a>>,
enum_names: Vec<EnumRef<'a>>,
struct_declarations: Vec<StructDeclarationRef<'a>>,
global_function_declarations: Vec<SignatureRef<'a>>,
cef_name_map: BTreeMap<String, NameMapEntry>,
rust_name_map: BTreeMap<String, NameMapEntry>,
lookup_type_alias: BTreeMap<String, usize>,
lookup_enum_name: BTreeMap<String, usize>,
lookup_struct_declaration: BTreeMap<String, usize>,
lookup_global_function_declaration: BTreeMap<String, usize>,
base_types: BTreeMap<String, String>,
}
impl ParseTree<'_> {
pub fn write_prelude(&self, f: &mut Formatter<'_>) -> fmt::Result {
let header = quote! {
#![allow(
dead_code,
improper_ctypes_definitions,
non_camel_case_types,
unused_variables,
clippy::not_unsafe_ptr_arg_deref,
clippy::too_many_arguments
)]
use crate::rc::{
ConvertParam, ConvertReturnValue, Rc, RcImpl, RefGuard, WrapParamRef,
};
use cef_dll_sys::*;
}
.to_string();
writeln!(f, "{header}")?;
writeln!(
f,
"\n/// Perform the conversion between CEF and Rust types in field initializers."
)?;
let init_array_field = quote! {
fn init_array_field<T, U, const N: usize>(mut value: [U; N]) -> [T; N]
where
T: Sized,
U: Sized + Into<T>,
{
std::array::from_fn(move |i| {
let mut elem = unsafe { std::mem::zeroed() };
std::mem::swap(&mut value[i], &mut elem);
elem.into()
})
}
}
.to_string();
writeln!(f, "{init_array_field}")
}
fn resolve_type_aliases(&self, ty: &syn::Type) -> proc_macro2::TokenStream {
match ty {
syn::Type::Path(syn::TypePath { qself: None, path }) => {
let ty = path.to_token_stream().to_string();
if is_custom_string_userfree_alias(ty.as_str()) {
path.to_token_stream()
} else {
match self.cef_name_map.get(&ty) {
Some(NameMapEntry {
ty: NameMapType::TypeAlias,
..
}) => self
.lookup_type_alias
.get(&ty)
.and_then(|&i| self.type_aliases.get(i))
.map(|alias| self.resolve_type_aliases(alias.ty))
.unwrap_or_else(|| path.to_token_stream()),
_ => path.to_token_stream(),
}
}
}
syn::Type::Tuple(syn::TypeTuple { elems, .. }) => {
let elems = elems.iter().map(|elem| self.resolve_type_aliases(elem));
quote! { #(#elems),* }
}
syn::Type::Array(syn::TypeArray { elem, len, .. }) => {
let elem = self.resolve_type_aliases(elem);
let len = len.to_token_stream();
quote! { [#elem; #len] }
}
syn::Type::Slice(syn::TypeSlice { elem, .. }) => {
let elem = self.resolve_type_aliases(elem);
quote! { [#elem] }
}
syn::Type::Ptr(syn::TypePtr {
const_token, elem, ..
}) => {
let elem = self.resolve_type_aliases(elem.as_ref());
if const_token.is_some() {
quote! { *const #elem }
} else {
quote! { *mut #elem }
}
}
_ => ty.to_token_stream(),
}
}
fn resolve_modified_type(&self, ty: &syn::Type) -> Option<ModifiedType> {
let ty = self.resolve_type_aliases(ty);
syn::parse2::<ModifiedType>(ty.clone()).ok()
}
pub fn write_aliases(&self, f: &mut Formatter<'_>) -> fmt::Result {
for TypeAliasRef { name, ty } in self.type_aliases.iter() {
let comment_ty: String = syn::parse2::<ModifiedType>(ty.to_token_stream())
.map(|ty| ty.ty.to_token_stream())
.unwrap_or_else(|_| ty.to_token_stream())
.to_string()
.split_whitespace()
.flat_map(|word| word.chars())
.collect();
let comment = format!("See [`{comment_ty}`] for more documentation.");
let (Some(rust_name), Some(arg_ty)) = (
make_rust_type_name(name.as_str()),
self.resolve_modified_type(ty),
) else {
continue;
};
let ty = arg_ty.ty.to_token_stream().to_string();
let rust_name_ident = format_ident!("{rust_name}");
if CUSTOM_STRING_USERFREE_ALIASES.contains(&name.as_str()) {
writeln!(f, "\n/// {comment}")?;
self.write_custom_string_type(f, &rust_name_ident)?;
continue;
}
if ty == quote! { ::std::os::raw::c_void }.to_string() {
continue;
}
let ty = make_rust_type_name(&ty).unwrap_or(ty);
if rust_name == ty.as_str() {
continue;
}
let ty = syn::parse_str::<syn::Type>(&ty).unwrap_or(arg_ty.ty);
let modifiers = arg_ty
.modifiers
.iter()
.filter_map(|modifier| match modifier {
TypeModifier::MutPtr => Some(quote! { *mut }),
TypeModifier::ConstPtr => Some(quote! { *const }),
TypeModifier::MutRef => Some(quote! { &mut }),
TypeModifier::Ref => Some(quote! { & }),
_ => None,
});
let ty = match arg_ty.modifiers.last() {
Some(TypeModifier::MutSlice) => quote! { &mut [#ty] },
Some(TypeModifier::Slice) => quote! { &[#ty] },
Some(TypeModifier::Array { size }) => quote! { [#ty; #size] },
_ => ty.to_token_stream(),
};
let alias = quote! {
pub type #rust_name_ident = #(#modifiers)* #ty;
}
.to_string();
writeln!(f, "\n/// {comment}")?;
writeln!(f, "{alias}")?;
}
Ok(())
}
fn base(&self, name: &str) -> Option<&str> {
self.base_types.get(name).map(String::as_str)
}
fn root<'b: 'c, 'c>(&'b self, name: &'c str) -> &'c str {
self.base(name).map(|base| self.root(base)).unwrap_or(name)
}
fn write_custom_string_type(
&self,
f: &mut Formatter<'_>,
rust_name: &syn::Ident,
) -> fmt::Result {
let wrapper = quote! {
pub use crate::string::#rust_name;
}
.to_string();
writeln!(f, "{wrapper}")
}
fn write_sealed_struct(
&self,
f: &mut Formatter<'_>,
s: &StructDeclarationRef<'_>,
root: &str,
name_ident: &syn::Ident,
rust_name: &syn::Ident,
) -> fmt::Result {
let name = s.name.as_str();
let methods = s.methods.iter().map(|m| {
let sig = m.get_signature(self);
let name = &m.name;
let name = format_ident!("{name}");
let pre_forward_args = m.unwrap_rust_args(self);
let args: Vec<_> = m
.inputs
.iter()
.map(|arg| {
let name = make_snake_case_value_name(&arg.name);
let name = format_ident!("arg_{name}");
quote! { #name }
})
.collect();
let post_forward_args = m.rewrap_rust_args(self);
let output_type = m.output.and_then(|ty| {
let ty = self.resolve_type_aliases(ty);
syn::parse2::<ModifiedType>(ty.to_token_stream()).ok()
});
output_type
.as_ref()
.map(|ModifiedType { modifiers, ty, .. }| {
let ty = ty.to_token_stream().to_string();
let wrap_result = match modifiers.as_slice() {
[TypeModifier::ConstPtr | TypeModifier::MutPtr]
if ty != quote! { ::std::os::raw::c_void }.to_string() =>
{
match self.cef_name_map.get(&ty) {
Some(NameMapEntry {
ty: NameMapType::StructDeclaration(_),
..
}) => Some(quote! {
if result.is_null() {
None
} else {
Some(result.wrap_result())
}
}),
_ => None,
}
}
_ => None,
}
.unwrap_or(quote! { result.wrap_result() });
let impl_default = output_type
.as_ref()
.and_then(|ty| {
(ty.ty.to_token_stream().to_string()
!= quote! { ::std::os::raw::c_void }.to_string())
.then(|| quote! { .unwrap_or_default() })
})
.unwrap_or(quote! { .unwrap_or_else(|| std::mem::zeroed()) });
quote! {
#sig {
unsafe {
self.0.#name.map(|f| {
#pre_forward_args
let result = f(#(#args),*);
#post_forward_args
#wrap_result
})
#impl_default
}
}
}
})
.unwrap_or(quote! {
#sig {
unsafe {
if let Some(f) = self.0.#name {
#pre_forward_args
f(#(#args),*);
#post_forward_args
}
}
}
})
});
let base_name = self.base(name);
let impl_trait = format_ident!("Impl{rust_name}");
let impl_base_name = base_name
.filter(|base| *base != BASE_REF_COUNTED)
.and_then(|base| self.cef_name_map.get(base))
.map(|entry| {
let base = &entry.name;
let base = format_ident!("Impl{base}");
quote! { #base }
});
let impl_get_raw = impl_base_name
.as_ref()
.map(|impl_base_name| {
quote! {
fn get_raw(&self) -> *mut #name_ident {
<Self as #impl_base_name>::get_raw(self).cast()
}
}
})
.unwrap_or(quote! { fn get_raw(&self) -> *mut #name_ident; });
let impl_base_name = impl_base_name.unwrap_or(quote! { Clone + Sized + Rc });
let impl_methods = s.methods.iter().map(|m| {
let sig = m.get_signature(self);
let method_name = &m.name;
let comment = format!("See [`{name}::{method_name}`] for more documentation.");
quote! {
#[doc = #comment]
#sig;
}
});
let mut base_name = base_name;
let mut base_structs = vec![];
while let Some(next_base) = base_name
.filter(|base| *base != root)
.and_then(|base| self.lookup_struct_declaration.get(base))
.and_then(|&i| self.struct_declarations.get(i))
{
base_name = self.base(&next_base.name);
base_structs.push(next_base);
}
let impl_bases = base_structs
.into_iter()
.filter_map(|base_struct| {
self.cef_name_map.get(&base_struct.name).map(|entry| {
let base = &base_struct.name;
let base_ident = format_ident!("{base}");
let base = &entry.name;
let base_trait = format_ident!("Impl{base}");
let base = format_ident!("{base}");
let base_methods = base_struct.methods.iter().map(|m| {
let sig = m.get_signature(self);
let name = make_rust_method_name(&m.name);
let name = format_ident!("{name}");
let args = m.merge_params(self).filter_map(|arg| match arg {
MergedParam::Single { name, .. } => {
let name = format_ident!("{name}");
Some(quote! { #name })
}
MergedParam::Bounded { slice_name, .. }
| MergedParam::Buffer { slice_name, .. } => {
let name = format_ident!("{slice_name}");
Some(quote! { #name })
}
_ => None,
});
quote! {
#sig {
#base::from(self).#name(#(#args),*)
}
}
});
quote! {
impl #base_trait for #rust_name {
#(#base_methods)*
fn get_raw(&self) -> *mut #base_ident {
unsafe { RefGuard::into_raw(&self.0).cast() }
}
}
impl std::convert::From<&#rust_name> for #base {
fn from(from: &#rust_name) -> Self {
#base(unsafe {
RefGuard::from_raw_add_ref(RefGuard::into_raw(&from.0).cast())
})
}
}
}
})
})
.collect::<Vec<_>>()
.into_iter()
.rev();
let base_ident = format_ident!("{BASE_REF_COUNTED}");
let wrapper = quote! {
#[derive(Clone)]
pub struct #rust_name(RefGuard<#name_ident>);
pub trait #impl_trait : #impl_base_name {
#(#impl_methods)*
#impl_get_raw
}
#(#impl_bases)*
impl #impl_trait for #rust_name {
#(#methods)*
fn get_raw(&self) -> *mut #name_ident {
unsafe { RefGuard::into_raw(&self.0) }
}
}
impl Rc for #name_ident {
fn as_base(&self) -> &#base_ident {
self.base.as_base()
}
}
impl Rc for #rust_name {
fn as_base(&self) -> &#base_ident {
self.0.as_base()
}
}
impl ConvertParam<*mut #name_ident> for &#rust_name {
fn into_raw(self) -> *mut #name_ident {
#impl_trait::get_raw(self)
}
}
impl ConvertParam<*mut #name_ident> for &mut #rust_name {
fn into_raw(self) -> *mut #name_ident {
#impl_trait::get_raw(self)
}
}
impl ConvertReturnValue<#rust_name> for *mut #name_ident {
fn wrap_result(self) -> #rust_name {
#rust_name(unsafe { RefGuard::from_raw(self) })
}
}
impl From<#rust_name> for *mut #name_ident {
fn from(value: #rust_name) -> Self {
let object = #impl_trait::get_raw(&value);
std::mem::forget(value);
object
}
}
impl Default for #rust_name {
fn default() -> Self {
unsafe { std::mem::zeroed() }
}
}
}
.to_string();
writeln!(f, "{wrapper}")
}
fn write_ref_counted_struct(
&self,
f: &mut Formatter<'_>,
s: &StructDeclarationRef<'_>,
root: &str,
name_ident: &syn::Ident,
rust_name: &syn::Ident,
) -> fmt::Result {
if BASE_REF_COUNTED == s.name {
let wrapper = quote! {
#[derive(Clone)]
pub struct #rust_name(RefGuard<#name_ident>);
impl #rust_name {
fn get_raw(&self) -> *mut #name_ident {
unsafe { RefGuard::into_raw(&self.0) }
}
}
impl Rc for #rust_name {
fn as_base(&self) -> &#name_ident {
self.0.as_base()
}
}
impl ConvertParam<*mut #name_ident> for &#rust_name {
fn into_raw(self) -> *mut #name_ident {
self.get_raw()
}
}
impl ConvertParam<*mut #name_ident> for &mut #rust_name {
fn into_raw(self) -> *mut #name_ident {
self.get_raw()
}
}
impl ConvertReturnValue<#rust_name> for *mut #name_ident {
fn wrap_result(self) -> #rust_name {
#rust_name(unsafe { RefGuard::from_raw(self) })
}
}
impl From<#rust_name> for *mut #name_ident {
fn from(value: #rust_name) -> Self {
let object = value.get_raw();
std::mem::forget(value);
object
}
}
impl Default for #rust_name {
fn default() -> Self {
Self(unsafe { RefGuard::from_raw(std::ptr::null_mut()) })
}
}
}
.to_string();
return writeln!(f, "{wrapper}");
}
let name = s.name.as_str();
let methods = s.methods.iter().map(|m| {
let sig = m.get_signature(self);
let name = &m.name;
let name = format_ident!("{name}");
let pre_forward_args = m.unwrap_rust_args(self);
let args: Vec<_> = m
.inputs
.iter()
.map(|arg| {
let name = make_snake_case_value_name(&arg.name);
let name = format_ident!("arg_{name}");
quote! { #name }
})
.collect();
let post_forward_args = m.rewrap_rust_args(self);
let output_type = m.output.and_then(|ty| {
let ty = self.resolve_type_aliases(ty);
syn::parse2::<ModifiedType>(ty.to_token_stream()).ok()
});
output_type
.as_ref()
.map(|ModifiedType { modifiers, ty, .. }| {
let ty = ty.to_token_stream().to_string();
let wrap_result = match modifiers.as_slice() {
[TypeModifier::ConstPtr | TypeModifier::MutPtr]
if ty != quote! { ::std::os::raw::c_void }.to_string() =>
{
match self.cef_name_map.get(&ty) {
Some(NameMapEntry {
ty: NameMapType::StructDeclaration(_),
..
}) => Some(quote! {
if result.is_null() {
None
} else {
Some(result.wrap_result())
}
}),
_ => None,
}
}
_ => None,
}
.unwrap_or(quote! { result.wrap_result() });
let impl_default = output_type
.as_ref()
.and_then(|ty| {
(ty.ty.to_token_stream().to_string()
!= quote! { ::std::os::raw::c_void }.to_string())
.then(|| quote! { .unwrap_or_default() })
})
.unwrap_or(quote! { .unwrap_or_else(|| std::mem::zeroed()) });
quote! {
#sig {
unsafe {
self.0.#name.map(|f| {
#pre_forward_args
let result = f(#(#args),*);
#post_forward_args
#wrap_result
})
#impl_default
}
}
}
})
.unwrap_or(quote! {
#sig {
unsafe {
if let Some(f) = self.0.#name {
#pre_forward_args
f(#(#args),*);
#post_forward_args
}
}
}
})
});
let base_name = self.base(name);
let impl_trait = format_ident!("Impl{rust_name}");
let wrap_trait = format_ident!("Wrap{rust_name}");
let impl_base_name = base_name
.filter(|base| *base != BASE_REF_COUNTED)
.and_then(|base| self.cef_name_map.get(base))
.map(|entry| {
let base = &entry.name;
let base = format_ident!("Impl{base}");
quote! { #base }
});
let impl_get_raw = impl_base_name
.as_ref()
.map(|impl_base_name| {
quote! {
fn get_raw(&self) -> *mut #name_ident {
<Self as #impl_base_name>::get_raw(self).cast()
}
}
})
.unwrap_or(quote! { fn get_raw(&self) -> *mut #name_ident; });
let impl_base_name = impl_base_name.unwrap_or(quote! { Clone + Sized + Rc });
let impl_methods = s.methods.iter().map(|m| {
let sig = m.get_signature(self);
let method_name = &m.name;
let comment = format!("See [`{name}::{method_name}`] for more documentation.");
let impl_default =
m.output.map(
|ty| match syn::parse2::<ModifiedType>(ty.to_token_stream()) {
Ok(ty)
if ty.ty.to_token_stream().to_string()
!= quote! { ::std::os::raw::c_void }.to_string() =>
{
quote! { Default::default() }
}
_ => quote! { unsafe { std::mem::zeroed() } },
},
);
quote! {
#[doc = #comment]
#sig {
#impl_default
}
}
});
let mut base_name = base_name;
let mut base_structs = vec![];
while let Some(next_base) = base_name
.filter(|base| *base != root)
.and_then(|base| self.lookup_struct_declaration.get(base))
.and_then(|&i| self.struct_declarations.get(i))
{
base_name = self.base(&next_base.name);
base_structs.push(next_base);
}
let init_bases = base_structs
.iter()
.enumerate()
.map(|(i, base_struct)| {
let name = &base_struct.name;
let name = format_ident!("{name}");
let impl_mod = format_ident!("impl{name}");
let bases = iter::repeat_n(format_ident!("base"), i + 1);
quote! {
#impl_mod::init_methods::<Self>(&mut object.#(#bases).*);
}
})
.collect::<Vec<_>>()
.into_iter()
.rev();
let impl_bases = base_structs
.into_iter()
.filter_map(|base_struct| {
self.cef_name_map.get(&base_struct.name).map(|entry| {
let base = &base_struct.name;
let base_ident = format_ident!("{base}");
let base = &entry.name;
let base_trait = format_ident!("Impl{base}");
let base = format_ident!("{base}");
let base_methods = base_struct.methods.iter().map(|m| {
let sig = m.get_signature(self);
let name = make_rust_method_name(&m.name);
let name = format_ident!("{name}");
let args = m.merge_params(self).filter_map(|arg| match arg {
MergedParam::Single { name, .. } => {
let name = format_ident!("{name}");
Some(quote! { #name })
}
MergedParam::Bounded { slice_name, .. }
| MergedParam::Buffer { slice_name, .. } => {
let name = format_ident!("{slice_name}");
Some(quote! { #name })
}
_ => None,
});
quote! {
#sig {
#base(unsafe {
RefGuard::from_raw_add_ref(RefGuard::into_raw(&self.0).cast())
})
.#name(#(#args),*)
}
}
});
quote! {
impl #base_trait for #rust_name {
#(#base_methods)*
fn get_raw(&self) -> *mut #base_ident {
unsafe { RefGuard::into_raw(&self.0).cast() }
}
}
}
})
})
.collect::<Vec<_>>()
.into_iter()
.rev();
let name = &s.name;
let impl_mod = format_ident!("impl{name}");
let init_methods = s.methods.iter().map(|m| {
let name = &m.name;
let name = format_ident!("{name}");
quote! {
object.#name = Some(#name::<I>);
}
});
let wrapped_methods = s.methods.iter().map(|m| {
let name = &m.name;
let rust_method_name = make_rust_method_name(name);
let name = format_ident!("{name}");
let rust_method_name = format_ident!("{rust_method_name}");
let args = m.inputs.iter().map(|arg| {
let name = make_snake_case_value_name(&arg.name);
let name = format_ident!("{name}");
let ty = self.resolve_type_aliases(arg.ty);
quote! { #name: #ty }
});
let wrapped_args = m.wrap_cef_args(self);
let unwrapped_args = m.unwrap_cef_args(self);
let forward_args = m.merge_params(self).filter_map(|arg| match arg {
MergedParam::Single { name, .. } => {
let name = format_ident!("arg_{name}");
Some(quote! { #name })
}
MergedParam::Bounded { slice_name, .. }
| MergedParam::Buffer { slice_name, .. } => {
let name = format_ident!("arg_{slice_name}");
Some(quote! { #name })
}
_ => None,
});
let original_output = m.output.map(|ty| self.resolve_type_aliases(ty));
let output = original_output.as_ref().map(|output| {
quote! { -> #output }
});
let forward_output = original_output.and_then(|output| {
match syn::parse2::<ModifiedType>(output) {
Ok(ModifiedType { ty, modifiers }) => {
self.cef_name_map
.get(&ty.to_token_stream().to_string())
.map(|entry| match entry {
NameMapEntry {
ty: NameMapType::StructDeclaration(_),
..
} => match modifiers.as_slice() {
[TypeModifier::ConstPtr] => {
quote! { result.map(|result| result.into()).unwrap_or(std::ptr::null()) }
}
[TypeModifier::MutPtr] => {
quote! { result.map(|result| result.into()).unwrap_or(std::ptr::null_mut()) }
}
_ => quote! { result.into() },
}
_ => quote! { result.into() },
})
.or_else(|| {
if unwrapped_args.is_empty() {
None
} else {
Some(quote! { result })
}
})
}
_ => None,
}
});
let mut call_impl =
quote! { #impl_trait::#rust_method_name(&arg_self_.interface, #(#forward_args),*) };
if forward_output.is_some() || !unwrapped_args.is_empty() {
call_impl = quote! { let result = #call_impl; };
}
quote! {
extern "C" fn #name<I: #impl_trait>(#(#args),*) #output {
#wrapped_args
#call_impl
#unwrapped_args
#forward_output
}
}
});
let base_ident = format_ident!("{BASE_REF_COUNTED}");
let wrapper = quote! {
#[derive(Clone)]
pub struct #rust_name(RefGuard<#name_ident>);
impl #rust_name {
pub fn new<T>(interface: T) -> Self
where
T: #wrap_trait
{
unsafe {
let mut cef_object = std::mem::zeroed();
<T as #impl_trait>::init_methods(&mut cef_object);
let object = RcImpl::new(cef_object, interface);
<T as #wrap_trait>::wrap_rc(&mut (*object).interface, object);
let object: *mut #name_ident = object.cast();
object.wrap_result()
}
}
}
pub trait #wrap_trait : #impl_trait {
fn wrap_rc(&mut self, object: *mut RcImpl<#name_ident, Self>);
}
pub trait #impl_trait : #impl_base_name {
#(#impl_methods)*
fn init_methods(object: &mut #name_ident) {
#(#init_bases)*
#impl_mod::init_methods::<Self>(object);
}
#impl_get_raw
}
mod #impl_mod {
use super::*;
pub fn init_methods<I: #impl_trait>(object: &mut #name_ident) {
#(#init_methods)*
}
#(#wrapped_methods)*
}
#(#impl_bases)*
impl #impl_trait for #rust_name {
#(#methods)*
fn get_raw(&self) -> *mut #name_ident {
unsafe { RefGuard::into_raw(&self.0) }
}
}
impl Rc for #name_ident {
fn as_base(&self) -> &#base_ident {
self.base.as_base()
}
}
impl Rc for #rust_name {
fn as_base(&self) -> &#base_ident {
self.0.as_base()
}
}
impl ConvertParam<*mut #name_ident> for &#rust_name {
fn into_raw(self) -> *mut #name_ident {
#impl_trait::get_raw(self)
}
}
impl ConvertParam<*mut #name_ident> for &mut #rust_name {
fn into_raw(self) -> *mut #name_ident {
#impl_trait::get_raw(self)
}
}
impl ConvertReturnValue<#rust_name> for *mut #name_ident {
fn wrap_result(self) -> #rust_name {
#rust_name(unsafe { RefGuard::from_raw(self) })
}
}
impl From <#rust_name> for *mut #name_ident {
fn from(value: #rust_name) -> Self {
let object = #impl_trait::get_raw(&value);
std::mem::forget(value);
object
}
}
impl Default for #rust_name {
fn default() -> Self {
unsafe { std::mem::zeroed() }
}
}
}
.to_string();
writeln!(f, "{wrapper}")
}
fn write_scoped_struct(
&self,
f: &mut Formatter<'_>,
s: &StructDeclarationRef<'_>,
root: &str,
name_ident: &syn::Ident,
rust_name: &syn::Ident,
) -> fmt::Result {
if BASE_SCOPED == s.name {
let wrapper = quote! {
#[derive(Clone, Copy)]
pub struct #rust_name(*mut #name_ident);
impl #rust_name {
fn get_raw(&self) -> *mut #name_ident {
self.0
}
}
impl ConvertParam<*mut #name_ident> for &#rust_name {
fn into_raw(self) -> *mut #name_ident {
self.get_raw()
}
}
impl ConvertParam<*mut #name_ident> for &mut #rust_name {
fn into_raw(self) -> *mut #name_ident {
self.get_raw()
}
}
impl ConvertReturnValue<#rust_name> for *mut #name_ident {
fn wrap_result(self) -> #rust_name {
#rust_name(self)
}
}
impl From<#rust_name> for *mut #name_ident {
fn from(value: #rust_name) -> Self {
value.get_raw()
}
}
impl Default for #rust_name {
fn default() -> Self {
Self(std::ptr::null_mut())
}
}
}
.to_string();
return writeln!(f, "{wrapper}");
}
let name = s.name.as_str();
let methods = s.methods.iter().map(|m| {
let sig = m.get_signature(self);
let name = &m.name;
let name = format_ident!("{name}");
let pre_forward_args = m.unwrap_rust_args(self);
let args: Vec<_> = m
.inputs
.iter()
.map(|arg| {
let name = make_snake_case_value_name(&arg.name);
let name = format_ident!("arg_{name}");
quote! { #name }
})
.collect();
let post_forward_args = m.rewrap_rust_args(self);
let output_type = m.output.and_then(|ty| {
let ty = self.resolve_type_aliases(ty);
syn::parse2::<ModifiedType>(ty.to_token_stream()).ok()
});
output_type
.as_ref()
.map(|ModifiedType { modifiers, ty, .. }| {
let ty = ty.to_token_stream().to_string();
let wrap_result = match modifiers.as_slice() {
[TypeModifier::ConstPtr | TypeModifier::MutPtr]
if ty != quote! { ::std::os::raw::c_void }.to_string() =>
{
match self.cef_name_map.get(&ty) {
Some(NameMapEntry {
ty: NameMapType::StructDeclaration(_),
..
}) => Some(quote! {
if result.is_null() {
None
} else {
Some(result.wrap_result())
}
}),
_ => None,
}
}
_ => None,
}
.unwrap_or(quote! { result.wrap_result() });
let impl_default = output_type
.as_ref()
.and_then(|ty| {
(ty.ty.to_token_stream().to_string()
!= quote! { ::std::os::raw::c_void }.to_string())
.then(|| quote! { .unwrap_or_default() })
})
.unwrap_or(quote! { .unwrap_or_else(|| std::mem::zeroed()) });
quote! {
#sig {
unsafe {
self.0.as_ref().and_then(|this| this.#name).map(|f| {
#pre_forward_args
let result = f(#(#args),*);
#post_forward_args
#wrap_result
})
#impl_default
}
}
}
})
.unwrap_or(quote! {
#sig {
unsafe {
if let Some(f) = self.0.as_ref().and_then(|this| this.#name) {
#pre_forward_args
f(#(#args),*);
#post_forward_args
}
}
}
})
});
let base_name = self.base(name);
let impl_trait = format_ident!("Impl{rust_name}");
let impl_base_name = base_name
.filter(|base| *base != BASE_SCOPED)
.and_then(|base| self.cef_name_map.get(base))
.map(|entry| {
let base = &entry.name;
let base = format_ident!("Impl{base}");
quote! { #base }
});
let impl_get_raw = impl_base_name
.as_ref()
.map(|impl_base_name| {
quote! {
fn get_raw(&self) -> *mut #name_ident {
<Self as #impl_base_name>::get_raw(self).cast()
}
}
})
.unwrap_or(quote! { fn get_raw(&self) -> *mut #name_ident; });
let impl_base_name = impl_base_name.unwrap_or(quote! { Sized });
let impl_methods = s.methods.iter().map(|m| {
let sig = m.get_signature(self);
let method_name = &m.name;
let comment = format!("See [`{name}::{method_name}`] for more documentation.");
quote! {
#[doc = #comment]
#sig;
}
});
let mut base_name = base_name;
let mut base_structs = vec![];
while let Some(next_base) = base_name
.filter(|base| *base != root)
.and_then(|base| self.lookup_struct_declaration.get(base))
.and_then(|&i| self.struct_declarations.get(i))
{
base_name = self.base(&next_base.name);
base_structs.push(next_base);
}
let init_bases = base_structs
.iter()
.enumerate()
.map(|(i, base_struct)| {
let name = &base_struct.name;
let name = format_ident!("{name}");
let impl_mod = format_ident!("impl{name}");
let bases = iter::repeat_n(format_ident!("base"), i + 1);
quote! {
#impl_mod::init_methods::<Self>(&mut object.#(#bases).*);
}
})
.collect::<Vec<_>>()
.into_iter()
.rev();
let impl_bases = base_structs
.into_iter()
.filter_map(|base_struct| {
self.cef_name_map.get(&base_struct.name).map(|entry| {
let name = &base_struct.name;
let name_ident = format_ident!("{name}");
let base = &entry.name;
let base_trait = format_ident!("Impl{base}");
let base = format_ident!("{base}");
let base_methods = base_struct.methods.iter().map(|m| {
let sig = m.get_signature(self);
let name = &m.name;
let name = format_ident!("{name}");
let args = m.merge_params(self).filter_map(|arg| match arg {
MergedParam::Single { name, .. } => {
let name = format_ident!("{name}");
Some(quote! { #name })
}
MergedParam::Bounded { slice_name, .. }
| MergedParam::Buffer { slice_name, .. } => {
let name = format_ident!("{slice_name}");
Some(quote! { #name })
}
_ => None,
});
quote! {
#sig {
#base(self.0.cast()).#name(#(#args),*)
}
}
});
quote! {
impl #base_trait for #rust_name {
#(#base_methods)*
fn get_raw(&self) -> *mut #name_ident {
self.0.cast()
}
}
}
})
})
.collect::<Vec<_>>()
.into_iter()
.rev();
let name = &s.name;
let impl_mod = format_ident!("impl{name}");
let init_methods = s.methods.iter().map(|m| {
let name = &m.name;
let name = format_ident!("{name}");
quote! {
object.#name = Some(#name::<I>);
}
});
let wrapped_methods = s.methods.iter().map(|m| {
let name = &m.name;
let rust_method_name = make_rust_method_name(name);
let name = format_ident!("{name}");
let rust_method_name = format_ident!("{rust_method_name}");
let args = m.inputs.iter().map(|arg| {
let name = make_snake_case_value_name(&arg.name);
let name = format_ident!("{name}");
let ty = self.resolve_type_aliases(arg.ty);
quote! { #name: #ty }
});
let wrapped_args = m.wrap_cef_args(self);
let unwrapped_args = m.unwrap_cef_args(self);
let forward_args = m.merge_params(self).filter_map(|arg| match arg {
MergedParam::Single { name, .. } => {
let name = format_ident!("arg_{name}");
Some(quote! { #name })
}
MergedParam::Bounded { slice_name, .. }
| MergedParam::Buffer { slice_name, .. } => {
let name = format_ident!("arg_{slice_name}");
Some(quote! { #name })
}
_ => None,
});
let original_output = m.output.map(|ty| self.resolve_type_aliases(ty));
let output = original_output.as_ref().map(|output| {
quote! { -> #output }
});
let forward_output = original_output.and_then(|output| {
match syn::parse2::<ModifiedType>(output) {
Ok(ModifiedType { ty, modifiers }) => {
self.cef_name_map
.get(&ty.to_token_stream().to_string())
.map(|entry| match entry {
NameMapEntry {
ty: NameMapType::StructDeclaration(_),
..
} => match modifiers.as_slice() {
[TypeModifier::ConstPtr] => {
quote! { result.map(|result| result.into()).unwrap_or(std::ptr::null()) }
}
[TypeModifier::MutPtr] => {
quote! { result.map(|result| result.into()).unwrap_or(std::ptr::null_mut()) }
}
_ => quote! { result.into() },
}
_ => quote! { result.into() },
})
.or_else(|| {
if unwrapped_args.is_empty() {
None
} else {
Some(quote! { result })
}
})
}
_ => None,
}
});
let mut call_impl =
quote! { #impl_trait::#rust_method_name(&arg_self_.interface, #(#forward_args),*) };
if forward_output.is_some() || !unwrapped_args.is_empty() {
call_impl = quote! { let result = #call_impl; };
}
quote! {
extern "C" fn #name<I: #impl_trait>(#(#args),*) #output {
#wrapped_args
#call_impl
#unwrapped_args
#forward_output
}
}
});
let wrapper = quote! {
#[derive(Clone, Copy)]
pub struct #rust_name(*mut #name_ident);
pub trait #impl_trait : #impl_base_name {
#(#impl_methods)*
fn init_methods(object: &mut #name_ident) {
#(#init_bases)*
#impl_mod::init_methods::<Self>(object);
}
#impl_get_raw
}
mod #impl_mod {
use super::*;
pub fn init_methods<I: #impl_trait>(object: &mut #name_ident) {
#(#init_methods)*
}
#(#wrapped_methods)*
}
#(#impl_bases)*
impl #impl_trait for #rust_name {
#(#methods)*
fn get_raw(&self) -> *mut #name_ident {
self.0
}
}
impl ConvertParam<*mut #name_ident> for &#rust_name {
fn into_raw(self) -> *mut #name_ident {
#impl_trait::get_raw(self)
}
}
impl ConvertParam<*mut #name_ident> for &mut #rust_name {
fn into_raw(self) -> *mut #name_ident {
#impl_trait::get_raw(self)
}
}
impl ConvertReturnValue<#rust_name> for *mut #name_ident {
fn wrap_result(self) -> #rust_name {
#rust_name(self)
}
}
impl From<#rust_name> for *mut #name_ident {
fn from(value: #rust_name) -> Self {
#impl_trait::get_raw(&value)
}
}
impl Default for #rust_name {
fn default() -> Self {
Self(std::ptr::null_mut())
}
}
}
.to_string();
writeln!(f, "{wrapper}")
}
pub fn write_structs(&self, f: &mut Formatter<'_>) -> fmt::Result {
for s in self.struct_declarations.iter() {
let Some(NameMapEntry {
name: rust_name,
ty: NameMapType::StructDeclaration(_),
}) = self.cef_name_map.get(&s.name)
else {
continue;
};
let rust_name = format_ident!("{rust_name}");
let name = s.name.as_str();
writeln!(f, "\n/// See [`{name}`] for more documentation.")?;
if CUSTOM_STRING_TYPES.contains(&name) {
self.write_custom_string_type(f, &rust_name)?;
continue;
}
let name_ident = format_ident!("{name}");
let root = self.root(name);
if root == BASE_REF_COUNTED {
if s.is_sealed {
self.write_sealed_struct(f, s, root, &name_ident, &rust_name)
} else {
self.write_ref_counted_struct(f, s, root, &name_ident, &rust_name)
}?;
continue;
}
if root == BASE_SCOPED {
self.write_scoped_struct(f, s, root, &name_ident, &rust_name)?;
continue;
}
if !s.methods.is_empty()
|| s.fields.is_empty()
|| s.fields.iter().map(|f| f.name.as_str()).eq(["_unused"])
{
let wrapper = quote! {
pub struct #rust_name(#name_ident);
impl From<#name_ident> for #rust_name {
fn from(value: #name_ident) -> Self {
Self(value)
}
}
impl From<&#rust_name> for *const #name_ident {
fn from(value: &#rust_name) -> Self {
std::ptr::from_ref(value).cast()
}
}
impl From<&mut #rust_name> for *mut #name_ident {
fn from(value: &mut #rust_name) -> Self {
std::ptr::from_mut(value).cast()
}
}
impl From<#rust_name> for #name_ident {
fn from(value: #rust_name) -> Self {
self.0
}
}
impl AsRef<#name_ident> for #rust_name {
fn as_ref(&self) -> &#name_ident {
&self.0
}
}
impl AsMut<#name_ident> for #rust_name {
fn as_mut(&mut self) -> &mut #name_ident {
&mut self.0
}
}
impl Default for #rust_name {
fn default() -> Self {
unsafe { std::mem::zeroed() }
}
}
}
.to_string();
writeln!(f, "{wrapper}")?;
continue;
}
let fields = s
.fields
.iter()
.map(|f| {
let name = &f.name;
let rust_name = make_snake_case_value_name(name);
let rust_name = format_ident!("{rust_name}");
let name = format_ident!("{name}");
let ty = self
.resolve_modified_type(f.ty)
.unwrap_or_else(|| ModifiedType {
modifiers: Default::default(),
ty: f.ty.clone(),
});
let rust_ty = ty.ty.to_token_stream();
let ty_string = rust_ty.to_string();
let entry = self.cef_name_map.get(&ty_string);
let rust_ty = match entry.as_ref() {
Some(NameMapEntry { name, .. }) => {
let name = format_ident!("{name}");
quote! { #name }
}
_ => rust_ty,
};
let modifiers = ty.modifiers.iter().filter_map(|modifier| match modifier {
TypeModifier::MutPtr => Some(quote! { *mut }),
TypeModifier::ConstPtr => Some(quote! { *const }),
TypeModifier::MutRef => Some(quote! { &mut }),
TypeModifier::Ref => Some(quote! { & }),
_ => None,
});
let rust_ty = match ty.modifiers.last() {
Some(TypeModifier::MutSlice) => quote! { &mut [#rust_ty] },
Some(TypeModifier::Slice) => quote! { &[#rust_ty] },
Some(TypeModifier::Array { size }) => quote! { [#rust_ty; #size] },
_ => rust_ty,
};
(
rust_name,
name.clone(),
entry,
quote! { #(#modifiers)* #rust_ty },
)
})
.collect::<Vec<_>>();
let fields_decl = fields.iter().map(|(rust_name, _, _, ty)| {
quote! { pub #rust_name: #ty, }
});
let from_cef_fields = fields.iter().filter_map(|(rust_name, name, entry, ty)| {
let ty = syn::parse2::<ModifiedType>(ty.clone()).ok()?;
Some(match (ty.modifiers.last(), entry) {
(Some(TypeModifier::Array { .. }), _) => {
quote! { #rust_name: init_array_field(value.#name), }
}
(_, Some(_)) => quote! { #rust_name: value.#name.into(), },
_ => quote! { #rust_name: value.#name, },
})
});
let from_rust_fields = fields.iter().filter_map(|(rust_name, name, entry, ty)| {
let ty = syn::parse2::<ModifiedType>(ty.clone()).ok()?;
Some(match (ty.modifiers.last(), entry) {
(Some(TypeModifier::Array { .. }), _) => {
quote! { #name: init_array_field(value.#rust_name), }
}
(_, Some(_)) => quote! { #name: value.#rust_name.into(), },
_ => quote! { #name: value.#rust_name, },
})
});
let impl_default = match s.fields.first() {
Some(f) if f.name.as_str() == "size" => {
quote! {
Self {
size: std::mem::size_of::<#name_ident>(),
..unsafe { std::mem::zeroed() }
}
}
}
_ => quote! { unsafe { std::mem::zeroed() } },
};
let wrapper = quote! {
#[derive(Clone)]
pub struct #rust_name {
#(#fields_decl)*
}
impl From<#name_ident> for #rust_name {
fn from(value: #name_ident) -> Self {
Self {
#(#from_cef_fields)*
}
}
}
impl From<#rust_name> for #name_ident {
fn from(value: #rust_name) -> Self {
Self {
#(#from_rust_fields)*
}
}
}
impl Default for #rust_name {
fn default() -> Self {
#impl_default
}
}
}
.to_string();
writeln!(f, "{wrapper}")?;
}
Ok(())
}
pub fn write_enums(&self, f: &mut Formatter<'_>) -> fmt::Result {
let enum_names = self
.enum_names
.iter()
.filter_map(|e| make_rust_type_name(&e.name).map(|rust_name| (rust_name, e)));
for (rust_name, e) in enum_names {
let name = &e.name;
writeln!(f, "\n/// See [`{name}`] for more documentation.")?;
let name = format_ident!("{name}");
let rust_name = format_ident!("{rust_name}");
let impl_default =
e.ty.and_then(|ty| ty.variants.first())
.map(|v| {
let v = &v.ident;
quote! { Self(#name::#v) }
})
.unwrap_or(quote! { unsafe { std::mem::zeroed() } });
let wrapper = quote! {
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct #rust_name(#name);
impl AsRef<#name> for #rust_name {
fn as_ref(&self) -> &#name {
&self.0
}
}
impl AsMut<#name> for #rust_name {
fn as_mut(&mut self) -> &mut #name {
&mut self.0
}
}
impl From<#name> for #rust_name {
fn from(value: #name) -> Self {
Self(value)
}
}
impl From<#rust_name> for #name {
fn from(value: #rust_name) -> Self {
value.0
}
}
impl Default for #rust_name {
fn default() -> Self {
#impl_default
}
}
}
.to_string();
writeln!(f, "{wrapper}")?;
}
Ok(())
}
pub fn write_globals(&self, f: &mut Formatter<'_>) -> fmt::Result {
let pattern = Regex::new(r"^cef_(\w+)$").unwrap();
for global_fn in self.global_function_declarations.iter() {
let original_name = global_fn.name.as_str();
writeln!(f, "\n/// See [`{original_name}`] for more documentation.")?;
let name = pattern
.captures(original_name)
.and_then(|captures| captures.get(1))
.map(|name| name.as_str())
.unwrap_or(original_name);
let name = format_ident!("{name}");
let original_name = format_ident!("{original_name}");
let args = global_fn.get_rust_args(self);
let output = global_fn.get_rust_output(self);
let inputs = global_fn
.inputs
.iter()
.map(|arg| {
let rust_name = make_snake_case_value_name(&arg.name);
let rust_name = format_ident!("arg_{rust_name}");
let ty = self.resolve_type_aliases(arg.ty);
(rust_name, ty)
})
.collect::<Vec<_>>();
let unwrap_args = global_fn.unwrap_rust_args(self);
let forward_args: Vec<_> = inputs
.iter()
.map(|(rust_name, _)| {
quote! { #rust_name }
})
.collect();
let rewrap_args = global_fn.rewrap_rust_args(self);
let wrapper = global_fn
.output
.as_ref()
.and_then(|ty| syn::parse2::<ModifiedType>(self.resolve_type_aliases(ty)).ok())
.map(|ModifiedType { modifiers, ty, .. }| {
let ty = ty.to_token_stream().to_string();
let wrap_result = match modifiers.as_slice() {
[TypeModifier::ConstPtr | TypeModifier::MutPtr]
if ty != quote! { ::std::os::raw::c_void }.to_string() =>
{
match self.cef_name_map.get(&ty) {
Some(NameMapEntry {
ty: NameMapType::StructDeclaration(_),
..
}) => Some(quote! {
if result.is_null() {
None
} else {
Some(result.wrap_result())
}
}),
_ => None,
}
}
_ => None,
}
.unwrap_or(quote! { result.wrap_result() });
quote! {
pub fn #name(#args) #output {
unsafe {
#unwrap_args
let result = #original_name(#(#forward_args),*);
#rewrap_args
#wrap_result
}
}
}
})
.unwrap_or(quote! {
pub fn #name(#args) {
unsafe {
#unwrap_args
#original_name(#(#forward_args),*);
#rewrap_args
}
}
})
.to_string();
writeln!(f, "{wrapper}")?;
}
Ok(())
}
}
impl Display for ParseTree<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.write_prelude(f)?;
self.write_aliases(f)?;
self.write_structs(f)?;
self.write_enums(f)?;
self.write_globals(f)
}
}
impl<'a> From<&'a syn::File> for ParseTree<'a> {
fn from(value: &'a syn::File) -> Self {
let mut tree = Self::default();
tree.type_aliases = value
.items
.iter()
.filter_map(|item| match item {
syn::Item::Type(item_type) => Some(TypeAliasRef {
name: item_type.ident.to_string(),
ty: item_type.ty.as_ref(),
}),
_ => None,
})
.collect();
tree.enum_names = value
.items
.iter()
.filter_map(|item| match item {
syn::Item::Enum(e) => Some(EnumRef {
name: e.ident.to_string(),
ty: Some(e),
}),
syn::Item::Struct(item_struct) => match &item_struct.fields {
syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => Some(EnumRef {
name: item_struct.ident.to_string(),
ty: None,
}),
_ => None,
},
_ => None,
})
.collect();
tree.struct_declarations = value
.items
.iter()
.filter_map(|item| match item {
syn::Item::Struct(item_struct) => match &item_struct.fields {
syn::Fields::Named(syn::FieldsNamed { named, .. }) => {
let mut fields = vec![];
let mut methods = vec![];
for member in named.iter() {
if let Ok(method) = SignatureRef::try_from(member) {
methods.push(method);
} else if let Ok(field) = FieldRef::try_from(member) {
fields.push(field);
}
}
let is_sealed = item_struct
.attrs
.iter()
.find_map(|attr| match attr {
syn::Attribute {
style: syn::AttrStyle::Outer,
meta:
syn::Meta::NameValue(syn::MetaNameValue {
path,
value:
syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(value),
..
}),
..
}),
..
} if path.to_token_stream().to_string()
== quote! { doc }.to_string() =>
{
Some(value.to_token_stream().to_string())
}
_ => None,
})
.map(|comment| {
comment.ends_with(r#"NOTE: This struct is allocated DLL-side.\n""#)
})
.unwrap_or_default();
Some(StructDeclarationRef {
name: item_struct.ident.to_string(),
fields,
methods,
is_sealed,
})
}
_ => None,
},
_ => None,
})
.collect();
tree.global_function_declarations = value
.items
.iter()
.filter_map(|item| match item {
syn::Item::ForeignMod(syn::ItemForeignMod {
unsafety: Some(_),
abi:
syn::Abi {
name: Some(abi), ..
},
items,
..
}) if abi.value() == "C" => Some(items),
_ => None,
})
.flat_map(|items| {
items.iter().filter_map(|item| match item {
syn::ForeignItem::Fn(syn::ForeignItemFn {
sig:
syn::Signature {
ident,
inputs,
output,
..
},
..
}) => Some(SignatureRef {
name: ident.to_string(),
inputs: inputs
.iter()
.map(|arg| match arg {
syn::FnArg::Receiver(_) => {
unreachable!("unexpected function receiver")
}
syn::FnArg::Typed(syn::PatType { pat, ty, .. }) => {
match pat.as_ref() {
syn::Pat::Ident(syn::PatIdent { ident, .. }) => FnArgRef {
name: ident.to_string(),
ty: ty.as_ref(),
},
_ => unreachable!("unexpected argument name type"),
}
}
})
.collect(),
output: match output {
syn::ReturnType::Default => None,
syn::ReturnType::Type(_, ty) => Some(ty.as_ref()),
},
merged_params: Default::default(),
}),
_ => None,
})
})
.collect();
tree.cef_name_map = tree
.type_aliases
.iter()
.map(|alias| alias.name.as_str())
.map(|cef_name| (cef_name, NameMapType::TypeAlias))
.chain(
tree.enum_names
.iter()
.map(|e| e.name.as_str())
.map(|cef_name| (cef_name, NameMapType::EnumName)),
)
.chain(
tree.struct_declarations
.iter()
.map(|s| (s.name.as_str(), s.is_sealed))
.map(|(cef_name, is_sealed)| {
(cef_name, NameMapType::StructDeclaration(is_sealed))
}),
)
.filter_map(|(cef_name, ty)| {
make_rust_type_name(cef_name).map(|rust_name| (cef_name, (rust_name, ty)))
})
.filter_map(|(cef_name, (rust_name, ty))| {
if cef_name == rust_name.as_str() {
None
} else {
Some((
cef_name.to_owned(),
NameMapEntry {
name: rust_name,
ty,
},
))
}
})
.collect();
tree.rust_name_map = tree
.cef_name_map
.iter()
.map(|(a, NameMapEntry { name: b, ty })| {
(
b.clone(),
NameMapEntry {
name: a.clone(),
ty: *ty,
},
)
})
.collect();
tree.lookup_type_alias = tree
.type_aliases
.iter()
.enumerate()
.map(|(index, alias)| (alias.name.clone(), index))
.collect();
tree.lookup_enum_name = tree
.enum_names
.iter()
.enumerate()
.map(|(index, e)| (e.name.clone(), index))
.collect();
tree.lookup_struct_declaration = tree
.struct_declarations
.iter()
.enumerate()
.map(|(index, s)| (s.name.clone(), index))
.collect();
tree.lookup_global_function_declaration = tree
.global_function_declarations
.iter()
.enumerate()
.map(|(index, f)| (f.name.clone(), index))
.collect();
tree.base_types = tree
.struct_declarations
.iter()
.filter_map(|s| match s.fields.as_slice() {
[FieldRef { name, ty }] if name.as_str() == "base" => {
Some((s.name.clone(), tree.resolve_type_aliases(ty).to_string()))
}
_ => None,
})
.collect();
tree
}
}
fn format_bindings(source_path: &Path) -> crate::Result<()> {
let mut cmd = Command::new("rustfmt");
cmd.arg(source_path);
cmd.output()?;
Ok(())
}
fn make_rust_type_name(name: &str) -> Option<String> {
static PATTERN: OnceLock<Regex> = OnceLock::new();
let pattern = PATTERN.get_or_init(|| Regex::new(r"^_?cef_(\w+)_t$").unwrap());
pattern
.captures(name)
.and_then(|captures| captures.get(1))
.map(|name| {
let name = name
.as_str()
.from_case(Case::Snake)
.to_case(Case::UpperCamel);
if name.starts_with("String") {
format!("Cef{name}")
} else {
name
}
})
}
fn make_snake_case_value_name(name: &str) -> String {
name.from_case(Case::Camel).to_case(Case::Snake)
}
const RUST_METHOD_EXCEPTIONS: &[&str] = &["get_type", "get_layout"];
fn make_rust_method_name(name: &str) -> &str {
if RUST_METHOD_EXCEPTIONS.contains(&name) {
name
} else {
name.trim_start_matches("get_")
}
}