windows-bindgen 0.66.0

Code generator for Windows metadata
Documentation
use super::*;

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct CppFn {
    pub namespace: &'static str,
    pub method: MethodDef,
}

impl Ord for CppFn {
    fn cmp(&self, other: &Self) -> Ordering {
        (self.method.name(), self.method).cmp(&(other.method.name(), other.method))
    }
}

impl PartialOrd for CppFn {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl CppFn {
    pub fn type_name(&self) -> TypeName {
        TypeName(self.namespace, self.method.name())
    }

    pub fn write_name(&self, config: &Config) -> TokenStream {
        self.type_name().write(config, &[])
    }

    fn write_extern_signature(&self, config: &Config<'_>, underlying_types: bool) -> TokenStream {
        let signature = self.method.signature(self.namespace, &[]);

        let params = signature.params.iter().map(|param| {
            let name = param.write_ident();
            let ty = if underlying_types {
                param.underlying_type().write_abi(config)
            } else {
                param.write_abi(config)
            };
            quote! { #name: #ty }
        });

        let return_sig = config.write_return_sig(self.method, &signature, underlying_types);

        let vararg = if config.sys && signature.call_flags.contains(MethodCallAttributes::VARARG) {
            quote! { , ... }
        } else {
            quote! {}
        };

        quote! {
            (#(#params),* #vararg) #return_sig
        }
    }

    pub fn write_fn_ptr(&self, config: &Config<'_>, underlying_types: bool) -> TokenStream {
        let ptr_name = self.method.name().to_string();
        let name = to_ident(&ptr_name);
        let abi = self.method.calling_convention();
        let signature = self.write_extern_signature(config, underlying_types);

        quote! {
            pub type #name = unsafe extern #abi fn #signature;
        }
    }

    pub fn write_link(&self, config: &Config, underlying_types: bool) -> TokenStream {
        let library = self.method.module_name();
        let symbol = self.method.import_name();
        let name = to_ident(self.method.name());
        let abi = self.method.calling_convention();
        let signature = self.write_extern_signature(config, underlying_types);
        let link = to_ident(config.link);

        if config.sys_fn_extern {
            quote! {
                unsafe extern #abi {
                    pub fn #name #signature;
                }
            }
        } else {
            link_fmt(quote! {
                #link::link!(#library #abi #symbol fn #name #signature);
            })
        }
    }

    pub fn write_cfg(&self, config: &Config) -> TokenStream {
        if !config.package {
            return quote! {};
        }

        Cfg::new(&self.dependencies(), config).write(config, false)
    }

    pub fn write(&self, config: &Config) -> TokenStream {
        let name = to_ident(self.method.name());
        let signature = self.method.signature(self.namespace, &[]);

        let link = self.write_link(config, false);
        let arches = write_arches(self.method);
        let cfg = self.write_cfg(config);
        let cfg = quote! { #arches #cfg };
        let window_long = self.write_window_long();

        if config.sys {
            let fn_ptr = if config.sys_fn_ptrs {
                let fn_ptr = self.write_fn_ptr(config, false);

                quote! {
                    #cfg
                    #fn_ptr
                }
            } else {
                quote! {}
            };

            return quote! {
                #fn_ptr
                #cfg
                #link
                #window_long
            };
        }

        let method = CppMethod::new(self.method, self.namespace);
        let args = method.write_args();
        let params = method.write_params(config);
        let generics = method.write_generics();
        let abi_return_type = method.write_return(config);
        let result = config.write_result();

        let wrapper = match method.return_hint {
            ReturnHint::Query(..) => {
                let where_clause = method.write_where(config, true);

                quote! {
                    #cfg
                    #[inline]
                    pub unsafe fn #name<#generics T>(#params) -> #result Result<T> #where_clause {
                        #link
                        let mut result__ = core::ptr::null_mut();
                        unsafe { #name(#args).and_then(||windows_core::Type::from_abi(result__)) }
                    }
                }
            }
            ReturnHint::QueryOptional(..) => {
                let where_clause = method.write_where(config, true);

                quote! {
                    #cfg
                    #[inline]
                    pub unsafe fn #name<#generics T>(#params result__: *mut Option<T>) -> #result Result<()> #where_clause {
                        #link
                        unsafe { #name(#args).ok() }
                    }
                }
            }
            ReturnHint::ResultValue => {
                let where_clause = method.write_where(config, false);
                let return_type = signature.params[signature.params.len() - 1].deref();
                let map = return_type.write_result_map();
                let return_type = return_type.write_name(config);

                quote! {
                    #cfg
                    #[inline]
                    pub unsafe fn #name<#generics>(#params) -> #result Result<#return_type> #where_clause {
                        #link
                        unsafe {
                            let mut result__ = core::mem::zeroed();
                            #name(#args).#map
                        }
                    }
                }
            }
            ReturnHint::ResultVoid => {
                let where_clause = method.write_where(config, false);

                quote! {
                    #cfg
                    #[inline]
                    pub unsafe fn #name<#generics>(#params) -> #result Result<()> #where_clause {
                        #link
                        unsafe { #name(#args).ok() }
                    }
                }
            }
            ReturnHint::ReturnValue => {
                let where_clause = method.write_where(config, false);

                let return_type =
                    method.signature.params[method.signature.params.len() - 1].deref();

                if return_type.is_interface() {
                    let return_type = return_type.write_name(config);

                    quote! {
                        #cfg
                        #[inline]
                        pub unsafe fn #name<#generics>(#params) -> #result Result<#return_type> #where_clause {
                            #link
                            unsafe {
                                let mut result__ = core::mem::zeroed();
                                #name(#args);
                                windows_core::Type::from_abi(result__)
                            }
                        }
                    }
                } else {
                    let map = if return_type.is_copyable() {
                        quote! { result__ }
                    } else {
                        quote! { core::mem::transmute(result__) }
                    };

                    let where_clause = method.write_where(config, false);
                    let return_type = return_type.write_name(config);

                    quote! {
                        #cfg
                        #[inline]
                        pub unsafe fn #name<#generics>(#params) -> #return_type #where_clause {
                            #link
                            unsafe {
                                let mut result__ = core::mem::zeroed();
                                #name(#args);
                                #map
                            }
                        }
                    }
                }
            }
            ReturnHint::ReturnStruct | ReturnHint::None => {
                let where_clause = method.write_where(config, false);

                if method.handle_last_error() {
                    let return_type = signature.return_type.write_name(config);

                    quote! {
                        #cfg
                        #[inline]
                        pub unsafe fn #name<#generics>(#params) -> #result Result<#return_type> #where_clause {
                            #link
                            let result__ = unsafe { #name(#args) };
                            (!result__.is_invalid()).then_some(result__).ok_or_else(windows_core::Error::from_thread)
                        }
                    }
                } else {
                    quote! {
                        #cfg
                        #[inline]
                        pub unsafe fn #name<#generics>(#params) #abi_return_type #where_clause {
                            #link
                            unsafe { #name(#args) }
                        }
                    }
                }
            }
        };

        quote! {
            #wrapper
            #window_long
        }
    }

    fn write_window_long(&self) -> TokenStream {
        match self.method.name() {
            "GetWindowLongPtrA" => quote! {
                #[cfg(target_pointer_width = "32")]
                pub use GetWindowLongA as GetWindowLongPtrA;
            },
            "GetWindowLongPtrW" => quote! {
                #[cfg(target_pointer_width = "32")]
                pub use GetWindowLongW as GetWindowLongPtrW;
            },
            "SetWindowLongPtrA" => quote! {
                #[cfg(target_pointer_width = "32")]
                pub use SetWindowLongA as SetWindowLongPtrA;
            },
            "SetWindowLongPtrW" => quote! {
                #[cfg(target_pointer_width = "32")]
                pub use SetWindowLongW as SetWindowLongPtrW;
            },
            _ => quote! {},
        }
    }
}

impl Dependencies for CppFn {
    fn combine(&self, dependencies: &mut TypeMap) {
        self.method
            .signature(self.namespace, &[])
            .combine(dependencies);

        let dependency = match self.method.name() {
            "GetWindowLongPtrA" => Some("GetWindowLongA"),
            "GetWindowLongPtrW" => Some("GetWindowLongW"),
            "SetWindowLongPtrA" => Some("SetWindowLongA"),
            "SetWindowLongPtrW" => Some("SetWindowLongW"),
            _ => None,
        };

        if let Some(dependency) = dependency {
            self.method
                .reader()
                .unwrap_full_name(self.namespace, dependency)
                .combine(dependencies);
        }
    }
}

impl Config<'_> {
    pub fn write_return_sig(
        &self,
        method: MethodDef,
        signature: &Signature,
        underlying_types: bool,
    ) -> TokenStream {
        match &signature.return_type {
            Type::Void => {
                if method.has_attribute("DoesNotReturnAttribute") {
                    quote! { -> ! }
                } else {
                    quote! {}
                }
            }
            ty => {
                let ty = if underlying_types {
                    ty.underlying_type().write_default(self)
                } else {
                    ty.write_default(self)
                };

                quote! { -> #ty }
            }
        }
    }
}

fn link_fmt(tokens: TokenStream) -> TokenStream {
    let mut tokens = tokens.0.replacen(" ! (  ", "!(", 1);
    tokens = tokens.replacen(" ( ", "(", 1);
    tokens = tokens.replace(" , ", ", ");
    tokens = tokens.replace(" )", ")");
    tokens.into()
}