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 { 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 { 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>, output: Option<&'a syn::Type>, merged_params: OnceCell>, } enum MergedParam { Receiver, Single { name: String, ty: Option, }, 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 { 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::>(); 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 { 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::>(); 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::(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::(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 { 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::>(); 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::(&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::>()) .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::>()) .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 { 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::(&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::>() }); 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::>() }); 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::(&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 { let name = value .ident .as_ref() .ok_or(Unrecognized::FieldType)? .to_string(); // Look for a type matching std::option::Option 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 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>, methods: Vec>, 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, ty: syn::Type, } impl ModifiedType { fn get_argument_type(&self, tree: &ParseTree) -> Option { 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]> } }), [TypeModifier::MutSlice] => { Some(quote! { Option<&mut Vec>> }) } _ => 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>> }) } _ => 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>> }), _ => 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::>(); 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::>(); if modifiers.iter().any(Option::is_none) { None } else { Some(quote! { #(#modifiers)* #elem}) } } } } fn get_output_type(&self, tree: &ParseTree) -> Option { 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 { let mut modifiers = vec![]; loop { let lookahead = input.lookahead1(); if lookahead.peek(syn::Token![*]) { let _ = input.parse::()?; let lookahead = input.lookahead1(); if lookahead.peek(syn::Token![const]) { let _ = input.parse::()?; modifiers.push(TypeModifier::ConstPtr) } else { let _ = input.parse::()?; modifiers.push(TypeModifier::MutPtr) } } else if lookahead.peek(syn::Token![&]) { let _ = input.parse::()?; let lookahead = input.lookahead1(); if lookahead.peek(syn::Token![mut]) { let _ = input.parse::()?; 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::()?; 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>, enum_names: Vec>, struct_declarations: Vec>, global_function_declarations: Vec>, cef_name_map: BTreeMap, rust_name_map: BTreeMap, lookup_type_alias: BTreeMap, lookup_enum_name: BTreeMap, lookup_struct_declaration: BTreeMap, lookup_global_function_declaration: BTreeMap, base_types: BTreeMap, } 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(mut value: [U; N]) -> [T; N] where T: Sized, U: Sized + Into, { 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 { let ty = self.resolve_type_aliases(ty); syn::parse2::(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::(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::(&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::(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 { ::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::>() .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::(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 { ::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::(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::(&mut object.#(#bases).*); } }) .collect::>() .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::>() .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::); } }); 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::(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(#(#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(interface: T) -> Self where T: #wrap_trait { unsafe { let mut cef_object = std::mem::zeroed(); ::init_methods(&mut cef_object); let object = RcImpl::new(cef_object, interface); ::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::(object); } #impl_get_raw } mod #impl_mod { use super::*; pub fn init_methods(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::(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 { ::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::(&mut object.#(#bases).*); } }) .collect::>() .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::>() .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::); } }); 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::(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(#(#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::(object); } #impl_get_raw } mod #impl_mod { use super::*; pub fn init_methods(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::>(); 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::(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::(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::>(); 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::(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 { static PATTERN: OnceLock = 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_") } }