windows-bindgen 0.41.0

Code gen support for the windows crate
Documentation
use super::*;

pub fn gen(gen: &Gen, def: TypeDef) -> TokenStream {
    if gen.reader.type_def_flags(def).winrt() {
        gen_delegate(gen, def)
    } else {
        gen_callback(gen, def)
    }
}

fn gen_callback(gen: &Gen, def: TypeDef) -> TokenStream {
    let name = to_ident(gen.reader.type_def_name(def));

    let method = gen.reader.type_def_invoke_method(def);
    let signature = gen.reader.method_def_signature(method, &[]);
    let return_type = gen.return_sig(&signature);
    let cfg = gen.reader.type_def_cfg(def, &[]);
    let doc = gen.cfg_doc(&cfg);
    let features = gen.cfg_features(&cfg);

    let params = signature.params.iter().map(|p| {
        let name = gen.param_name(p.def);
        let tokens = gen.type_default_name(&p.ty);
        quote! { #name: #tokens }
    });

    quote! {
        #doc
        #features
        pub type #name = ::core::option::Option<unsafe extern "system" fn(#(#params),*) #return_type>;
    }
}

fn gen_delegate(gen: &Gen, def: TypeDef) -> TokenStream {
    if gen.sys {
        let name = to_ident(gen.reader.type_def_name(def));
        quote! {
            pub type #name = *mut ::core::ffi::c_void;
        }
    } else {
        gen_win_delegate(gen, def)
    }
}

fn gen_win_delegate(gen: &Gen, def: TypeDef) -> TokenStream {
    let name = to_ident(gen.reader.type_def_name(def));
    let vtbl = name.join("_Vtbl");
    let boxed = name.join("Box");

    let generics: &Vec<Type> = &gen.reader.type_def_generics(def).collect();
    let phantoms = gen.generic_phantoms(generics);
    let named_phantoms = gen.generic_named_phantoms(generics);
    let constraints = gen.generic_constraints(generics);
    let generic_names = gen.generic_names(generics);

    let ident = gen.type_def_name(def, generics);

    let method = gen.reader.type_def_invoke_method(def);
    let signature = gen.reader.method_def_signature(method, generics);
    let fn_constraint = gen_fn_constraint(gen, def, &signature);

    let cfg = gen.reader.type_def_cfg(def, generics);
    let doc = gen.cfg_doc(&cfg);
    let features = gen.cfg_features(&cfg);

    let vtbl_signature = gen.vtbl_signature(def, generics, &signature);
    let invoke = winrt_methods::gen(gen, def, generics, InterfaceKind::Default, method, &mut MethodNames::new(), &mut MethodNames::new());
    let invoke_upcall = winrt_methods::gen_upcall(gen, &signature, quote! { ((*this).invoke) });

    let mut tokens = quote! {
        #doc
        #features
        #[repr(transparent)]
        pub struct #ident(pub ::windows::core::IUnknown, #phantoms) where #constraints;
        #features
        impl<#constraints> #ident {
            pub fn new<#fn_constraint>(invoke: F) -> Self {
                let com = #boxed::<#generic_names F> {
                    vtable: &#boxed::<#generic_names F>::VTABLE,
                    count: ::windows::core::RefCount::new(1),
                    invoke,
                };
                unsafe {
                    ::core::mem::transmute(::windows::core::alloc::boxed::Box::new(com))
                }
            }
            #invoke
        }
        #features
        #[repr(C)]
        struct #boxed<#generic_names #fn_constraint> where #constraints {
            vtable: *const #vtbl<#generic_names>,
            invoke: F,
            count: ::windows::core::RefCount,
        }
        #features
        impl<#constraints #fn_constraint> #boxed<#generic_names F> {
            const VTABLE: #vtbl<#generic_names> = #vtbl::<#generic_names>{
                base__: ::windows::core::IUnknown_Vtbl{QueryInterface: Self::QueryInterface, AddRef: Self::AddRef, Release: Self::Release},
                Invoke: Self::Invoke,
                #(#named_phantoms)*
            };
            unsafe extern "system" fn QueryInterface(this: *mut ::core::ffi::c_void, iid: &::windows::core::GUID, interface: *mut *const ::core::ffi::c_void) -> ::windows::core::HRESULT {
                let this = this as *mut *mut ::core::ffi::c_void as *mut Self;

                *interface = if iid == &<#ident as ::windows::core::Interface>::IID ||
                    iid == &<::windows::core::IUnknown as ::windows::core::Interface>::IID ||
                    iid == &<::windows::core::IAgileObject as ::windows::core::Interface>::IID {
                        &mut (*this).vtable as *mut _ as _
                    } else {
                        ::core::ptr::null_mut()
                    };

                // TODO: implement IMarshal

                if (*interface).is_null() {
                    ::windows::core::HRESULT(-2147467262) // E_NOINTERFACE
                } else {
                    (*this).count.add_ref();
                    ::windows::core::HRESULT(0)
                }
            }
            unsafe extern "system" fn AddRef(this: *mut ::core::ffi::c_void) -> u32 {
                let this = this as *mut *mut ::core::ffi::c_void as *mut Self;
                (*this).count.add_ref()
            }
            unsafe extern "system" fn Release(this: *mut ::core::ffi::c_void) -> u32 {
                let this = this as *mut *mut ::core::ffi::c_void as *mut Self;
                let remaining = (*this).count.release();

                if remaining == 0 {
                    let _ = ::windows::core::alloc::boxed::Box::from_raw(this);
                }

                remaining
            }
            unsafe extern "system" fn Invoke #vtbl_signature {
                let this = this as *mut *mut ::core::ffi::c_void as *mut Self;
                #invoke_upcall
            }
        }
    };

    tokens.combine(&gen.interface_core_traits(def, generics, &ident, &constraints, &phantoms, &features));
    tokens.combine(&gen.interface_trait(def, generics, &ident, &constraints, &features));
    tokens.combine(&gen.interface_winrt_trait(def, generics, &ident, &constraints, &phantoms, &features));
    tokens.combine(&gen.interface_vtbl(def, generics, &ident, &constraints, &features));
    tokens
}

fn gen_fn_constraint(gen: &Gen, def: TypeDef, signature: &Signature) -> TokenStream {
    let signature = gen.impl_signature(def, signature);

    quote! { F: FnMut #signature + ::core::marker::Send + 'static }
}