blob: 30d4150e6615556a01b5ad927d084c5ab2345e7d [file] [log] [blame]
use super::*;
pub struct Gen<'a> {
pub reader: &'a Reader<'a>,
pub namespace: &'a str,
pub sys: bool,
pub cfg: bool,
pub doc: bool,
pub component: bool,
pub standalone: bool,
pub std: bool,
}
impl<'a> Gen<'a> {
pub fn new(reader: &'a Reader) -> Self {
Self {
reader,
namespace: "",
sys: false,
cfg: false,
doc: false,
component: false,
standalone: false,
std: false,
}
}
//
// TypeDef
//
pub fn type_def_name(&self, def: TypeDef, generics: &[Type]) -> TokenStream {
self.type_def_name_imp(def, generics, "")
}
pub fn type_def_vtbl_name(&self, def: TypeDef, generics: &[Type]) -> TokenStream {
self.type_def_name_imp(def, generics, "_Vtbl")
}
pub fn type_def_name_imp(&self, def: TypeDef, generics: &[Type], suffix: &str) -> TokenStream {
let type_name = self.reader.type_def_type_name(def);
if type_name.namespace.is_empty() {
to_ident(&self.scoped_name(def))
} else {
let mut namespace = self.namespace(type_name.namespace);
let mut name = to_ident(type_name.name);
name.push_str(suffix);
if generics.is_empty() || self.sys {
namespace.combine(&name);
namespace
} else {
let colon_separated = if !namespace.as_str().is_empty() {
quote! { :: }
} else {
quote! {}
};
let generics = generics.iter().map(|ty| self.type_name(ty));
quote! { #namespace #name #colon_separated<#(#generics),*> }
}
}
}
//
// Type
//
pub fn type_default_name(&self, ty: &Type) -> TokenStream {
if let Type::WinrtArray(ty) = ty {
self.type_default_name(ty)
} else {
let kind = self.type_name(ty);
if ty.is_generic() {
quote! { <#kind as ::windows::core::Type<#kind>>::Default }
} else if self.reader.type_is_nullable(ty) && !self.sys {
quote! { ::core::option::Option<#kind> }
} else {
kind
}
}
}
pub(crate) fn type_name(&self, ty: &Type) -> TokenStream {
match ty {
Type::Void => quote! { ::core::ffi::c_void },
Type::Bool => quote! { bool },
Type::Char => quote! { u16 },
Type::I8 => quote! { i8 },
Type::U8 => quote! { u8 },
Type::I16 => quote! { i16 },
Type::U16 => quote! { u16 },
Type::I32 => quote! { i32 },
Type::U32 => quote! { u32 },
Type::I64 => quote! { i64 },
Type::U64 => quote! { u64 },
Type::F32 => quote! { f32 },
Type::F64 => quote! { f64 },
Type::ISize => quote! { isize },
Type::USize => quote! { usize },
Type::String => {
let crate_name = self.crate_name();
quote! { #crate_name HSTRING }
}
Type::BSTR => {
let crate_name = self.crate_name();
quote! { #crate_name BSTR }
}
Type::IInspectable => {
let crate_name = self.crate_name();
quote! { #crate_name IInspectable }
}
Type::GUID => {
let crate_name = self.crate_name();
quote! { #crate_name GUID }
}
Type::IUnknown => {
let crate_name = self.crate_name();
quote! { #crate_name IUnknown }
}
Type::HRESULT => {
let crate_name = self.crate_name();
quote! { #crate_name HRESULT }
}
Type::PSTR => {
let crate_name = self.crate_name();
quote! { #crate_name PSTR }
}
Type::PWSTR => {
let crate_name = self.crate_name();
quote! { #crate_name PWSTR }
}
Type::PCSTR => {
let crate_name = self.crate_name();
quote! { #crate_name PCSTR }
}
Type::PCWSTR => {
let crate_name = self.crate_name();
quote! { #crate_name PCWSTR }
}
Type::Win32Array((ty, len)) => {
let name = self.type_default_name(ty);
let len = Literal::usize_unsuffixed(*len);
quote! { [#name; #len] }
}
Type::GenericParam(generic) => self.reader.generic_param_name(*generic).into(),
Type::TypeDef((def, generics)) => self.type_def_name(*def, generics),
Type::MutPtr((ty, pointers)) => {
let pointers = mut_ptrs(*pointers);
let ty = self.type_default_name(ty);
quote! { #pointers #ty }
}
Type::ConstPtr((ty, pointers)) => {
let pointers = const_ptrs(*pointers);
let ty = self.type_default_name(ty);
quote! { #pointers #ty }
}
Type::WinrtArray(ty) => self.type_name(ty),
Type::WinrtArrayRef(ty) => self.type_name(ty),
Type::WinrtConstRef(ty) => self.type_name(ty),
_ => unimplemented!(),
}
}
pub fn type_vtbl_name(&self, ty: &Type) -> TokenStream {
match ty {
Type::TypeDef((def, generics)) => self.type_def_vtbl_name(*def, generics),
_ => unimplemented!(),
}
}
pub fn type_abi_name(&self, ty: &Type) -> TokenStream {
if self.sys {
return self.type_default_name(ty);
}
match ty {
Type::IUnknown | Type::IInspectable => {
quote! { *mut ::core::ffi::c_void }
}
Type::String => {
quote! { ::std::mem::MaybeUninit<::windows::core::HSTRING> }
}
Type::BSTR => {
quote! { ::std::mem::MaybeUninit<::windows::core::BSTR> }
}
Type::Win32Array((kind, len)) => {
let name = self.type_abi_name(kind);
let len = Literal::usize_unsuffixed(*len);
quote! { [#name; #len] }
}
Type::GenericParam(generic) => {
let name = to_ident(self.reader.generic_param_name(*generic));
quote! { ::windows::core::AbiType<#name> }
}
Type::TypeDef((def, _)) => match self.reader.type_def_kind(*def) {
TypeKind::Enum => self.type_def_name(*def, &[]),
TypeKind::Struct => {
let tokens = self.type_def_name(*def, &[]);
if self.reader.type_def_is_blittable(*def) {
tokens
} else {
quote! { ::std::mem::MaybeUninit<#tokens> }
}
}
TypeKind::Delegate => {
if self
.reader
.type_def_flags(*def)
.contains(TypeAttributes::WINRT)
{
quote! { *mut ::core::ffi::c_void }
} else {
self.type_def_name(*def, &[])
}
}
_ => quote! { *mut ::core::ffi::c_void },
},
Type::MutPtr((kind, pointers)) => {
let pointers_tokens = gen_mut_ptrs(*pointers);
let kind = if *pointers > 1 {
self.type_name(kind)
} else {
self.type_abi_name(kind)
};
quote! { #pointers_tokens #kind }
}
Type::ConstPtr((kind, pointers)) => {
let pointers_tokens = gen_const_ptrs(*pointers);
let kind = if *pointers > 1 {
self.type_name(kind)
} else {
self.type_abi_name(kind)
};
quote! { #pointers_tokens #kind }
}
Type::WinrtArray(kind) => self.type_abi_name(kind),
Type::WinrtArrayRef(kind) => self.type_abi_name(kind),
_ => self.type_name(ty),
}
}
//
// Constraints
//
pub fn generic_phantoms(&self, generics: &[Type]) -> TokenStream {
let mut tokens = TokenStream::new();
for generic in generics {
let generic = self.type_name(generic);
tokens.combine(&quote! { ::core::marker::PhantomData::<#generic>, });
}
tokens
}
pub fn generic_named_phantoms(&self, generics: &[Type]) -> Vec<TokenStream> {
generics
.iter()
.map(|generic| {
let generic = self.type_name(generic);
quote! { #generic: ::core::marker::PhantomData::<#generic>, }
})
.collect()
}
pub fn generic_constraints(&self, generics: &[Type]) -> TokenStream {
let mut tokens = TokenStream::new();
for generic in generics {
let generic = self.type_name(generic);
tokens.combine(&quote! { #generic: ::windows::core::RuntimeType + 'static, });
}
tokens
}
pub fn generic_names(&self, generics: &[Type]) -> TokenStream {
let mut tokens = TokenStream::new();
for generic in generics {
let generic = self.type_name(generic);
tokens.combine(&quote! { #generic, });
}
tokens
}
/// The signature params which are generic (along with their relative index)
pub fn generic_params<'b>(
&'b self,
params: &'b [SignatureParam],
) -> impl Iterator<Item = (usize, &SignatureParam)> + 'b {
params
.iter()
.filter(move |param| self.reader.signature_param_is_convertible(param))
.enumerate()
}
/// The generic param names (i.e., `T` in `fn foo<T>()`)
pub fn constraint_generics(&self, params: &[SignatureParam]) -> TokenStream {
let mut generics = self
.generic_params(params)
.map(|(position, _)| -> TokenStream { format!("P{position}").into() })
.peekable();
if generics.peek().is_some() {
quote!(#(#generics),*)
} else {
TokenStream::new()
}
}
/// A `where` clause for some constrained generic params
pub fn where_clause(&self, params: &[SignatureParam]) -> TokenStream {
let constraints = self.param_constraints(params);
if !constraints.is_empty() {
quote!(where #constraints)
} else {
quote!()
}
}
fn param_constraints(&self, params: &[SignatureParam]) -> TokenStream {
let mut tokens = TokenStream::new();
let gen_name = |position| {
let name: TokenStream = format!("P{position}").into();
name
};
for (position, param) in self.generic_params(params) {
match param.kind {
SignatureParamKind::TryInto => {
let name: TokenStream = gen_name(position);
let into = self.type_name(&param.ty);
tokens.combine(&quote! { #name: ::windows::core::TryIntoParam<#into>, });
}
SignatureParamKind::IntoParam => {
let name: TokenStream = gen_name(position);
let into = self.type_name(&param.ty);
tokens.combine(&quote! { #name: ::windows::core::IntoParam<#into>, });
}
_ => {}
}
}
tokens
}
//
// Cfg
//
/// Generates doc comments for types, free functions, and constants.
pub(crate) fn cfg_doc(&self, cfg: &Cfg) -> TokenStream {
if !self.doc {
quote! {}
} else {
let mut tokens = format!(r#"`\"{}\"`"#, to_feature(self.namespace));
let features = self.cfg_features_imp(cfg, self.namespace);
for features in features {
write!(tokens, r#", `\"{}\"`"#, to_feature(features)).unwrap();
}
if cfg.implement {
tokens.push_str(r#", `\"implement\"`"#)
}
format!(r#" #[doc = "*Required features: {tokens}*"]"#).into()
}
}
/// Generates doc comments for member functions (methods) and avoids redundantly declaring the
/// enclosing module feature required by the method's type.
pub(crate) fn cfg_method_doc(&self, cfg: &Cfg) -> TokenStream {
if !self.doc {
quote! {}
} else {
let features = self.cfg_features_imp(cfg, self.namespace);
if features.is_empty() {
quote! {}
} else {
let mut tokens = String::new();
for features in features {
write!(tokens, r#"`\"{}\"`, "#, to_feature(features)).unwrap();
}
tokens.truncate(tokens.len() - 2);
format!(r#"#[doc = "*Required features: {tokens}*"]"#).into()
}
}
}
pub(crate) fn cfg_features(&self, cfg: &Cfg) -> TokenStream {
let arches = &cfg.arches;
let arch = match arches.len() {
0 => quote! {},
1 => {
quote! { #[cfg(#(target_arch = #arches),*)] }
}
_ => {
quote! { #[cfg(any(#(target_arch = #arches),*))] }
}
};
let features = self.cfg_features_imp(cfg, self.namespace);
let features = match features.len() {
0 => quote! {},
1 => {
let features = features.iter().cloned().map(to_feature);
quote! { #[cfg(#(feature = #features)*)] }
}
_ => {
let features = features.iter().cloned().map(to_feature);
quote! { #[cfg(all( #(feature = #features),* ))] }
}
};
quote! { #arch #features }
}
fn cfg_features_imp(&self, cfg: &'a Cfg, namespace: &'a str) -> Vec<&'a str> {
let mut compact = Vec::<&'static str>::new();
if !self.standalone && !self.component {
for feature in cfg.types.keys() {
if !feature.is_empty() && !starts_with(namespace, feature) {
for pos in 0..compact.len() {
if starts_with(feature, unsafe { compact.get_unchecked(pos) }) {
compact.remove(pos);
break;
}
}
compact.push(feature);
}
}
}
compact
}
fn cfg_not_features(&self, cfg: &Cfg) -> TokenStream {
let features = self.cfg_features_imp(cfg, self.namespace);
if features.is_empty() {
quote! {}
} else {
match features.len() {
0 => quote! {},
1 => {
let features = features.iter().cloned().map(to_feature);
quote! { #[cfg(not(#(feature = #features)*))] }
}
_ => {
let features = features.iter().cloned().map(to_feature);
quote! { #[cfg(not(all( #(feature = #features),* )))] }
}
}
}
}
//
// Other helpers
//
pub(crate) fn namespace(&self, namespace: &str) -> TokenStream {
if self.standalone || namespace == self.namespace {
quote! {}
} else {
let is_external =
namespace.starts_with("Windows.") && !self.namespace.starts_with("Windows");
let mut relative = self.namespace.split('.').peekable();
let mut namespace = namespace.split('.').peekable();
while relative.peek() == namespace.peek() {
if relative.next().is_none() {
break;
}
namespace.next();
}
let mut tokens = TokenStream::new();
if is_external {
tokens.push_str("::windows::");
namespace.next();
} else {
for _ in 0..relative.count() {
tokens.push_str("super::");
}
}
for namespace in namespace {
tokens.push_str(namespace);
tokens.push_str("::");
}
tokens
}
}
pub fn crate_name(&self) -> TokenStream {
if self.standalone {
TokenStream::new()
} else if self.sys {
"::windows_sys::core::".into()
} else {
"::windows::core::".into()
}
}
fn scoped_name(&self, def: TypeDef) -> String {
if let Some(enclosing_type) = self.reader.type_def_enclosing_type(def) {
for (index, nested_type) in self.reader.nested_types(enclosing_type).enumerate() {
if self.reader.type_def_name(nested_type) == self.reader.type_def_name(def) {
return format!("{}_{index}", &self.scoped_name(enclosing_type));
}
}
}
self.reader.type_def_name(def).to_string()
}
pub fn value(&self, value: &Value) -> TokenStream {
match value {
Value::Bool(value) => quote! { #value },
Value::U8(value) => quote! { #value },
Value::I8(value) => quote! { #value },
Value::U16(value) => quote! { #value },
Value::I16(value) => quote! { #value },
Value::U32(value) => quote! { #value },
Value::I32(value) => quote! { #value },
Value::U64(value) => quote! { #value },
Value::I64(value) => quote! { #value },
Value::F32(value) => quote! { #value },
Value::F64(value) => quote! { #value },
Value::String(value) => {
let mut tokens = "\"".to_string();
for u in value.chars() {
write!(tokens, "{}", u.escape_default()).unwrap();
}
tokens.push('\"');
tokens.into()
}
_ => unimplemented!(),
}
}
pub fn typed_value(&self, value: &Value) -> TokenStream {
let literal = self.value(value);
match value {
Value::Bool(_) => quote! { bool = #literal },
Value::U8(_) => quote! { u8 = #literal },
Value::I8(_) => quote! { i8 = #literal },
Value::U16(_) => quote! { u16 = #literal },
Value::I16(_) => quote! { i16 = #literal },
Value::U32(_) => quote! { u32 = #literal },
Value::I32(_) => quote! { i32 = #literal },
Value::U64(_) => quote! { u64 = #literal },
Value::I64(_) => quote! { i64 = #literal },
Value::F32(_) => quote! { f32 = #literal },
Value::F64(_) => quote! { f64 = #literal },
Value::String(_) => {
quote! { &str = #literal }
}
_ => unimplemented!(),
}
}
pub fn guid(&self, value: &GUID) -> TokenStream {
let guid = self.type_name(&Type::GUID);
format!("{}::from_u128(0x{:08x?}_{:04x?}_{:04x?}_{:02x?}{:02x?}_{:02x?}{:02x?}{:02x?}{:02x?}{:02x?}{:02x?})", guid.into_string(), value.0, value.1, value.2, value.3, value.4, value.5, value.6, value.7, value.8, value.9, value.10).into()
}
pub fn interface_core_traits(
&self,
def: TypeDef,
_generics: &[Type],
ident: &TokenStream,
constraints: &TokenStream,
_phantoms: &TokenStream,
features: &TokenStream,
) -> TokenStream {
let name = trim_tick(self.reader.type_def_name(def));
quote! {
#features
impl<#constraints> ::core::cmp::PartialEq for #ident {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
#features
impl<#constraints> ::core::cmp::Eq for #ident {}
#features
impl<#constraints> ::core::fmt::Debug for #ident {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.debug_tuple(#name).field(&self.0).finish()
}
}
}
}
pub fn agile(
&self,
def: TypeDef,
ident: &TokenStream,
constraints: &TokenStream,
features: &TokenStream,
) -> TokenStream {
if self.reader.type_def_is_agile(def) {
quote! {
#features
unsafe impl<#constraints> ::core::marker::Send for #ident {}
#features
unsafe impl<#constraints> ::core::marker::Sync for #ident {}
}
} else {
quote! {}
}
}
pub fn async_get(
&self,
def: TypeDef,
generics: &[Type],
ident: &TokenStream,
constraints: &TokenStream,
_phantoms: &TokenStream,
features: &TokenStream,
) -> TokenStream {
let mut kind = self.reader.type_def_async_kind(def);
let mut async_generics = generics.to_vec();
if kind == AsyncKind::None {
for interface in self.reader.type_def_interfaces(def, generics) {
if let Type::TypeDef((interface_def, interface_generics)) = &interface.ty {
kind = self.reader.type_def_async_kind(*interface_def);
if kind != AsyncKind::None {
async_generics = interface_generics.to_vec();
break;
}
}
}
}
if kind == AsyncKind::None {
quote! {}
} else {
let return_type = match kind {
AsyncKind::Operation | AsyncKind::OperationWithProgress => {
self.type_name(&async_generics[0])
}
_ => quote! { () },
};
let handler = match kind {
AsyncKind::Action => quote! { AsyncActionCompletedHandler },
AsyncKind::ActionWithProgress => quote! { AsyncActionWithProgressCompletedHandler },
AsyncKind::Operation => quote! { AsyncOperationCompletedHandler },
AsyncKind::OperationWithProgress => {
quote! { AsyncOperationWithProgressCompletedHandler }
}
_ => unimplemented!(),
};
let namespace = self.namespace("Windows.Foundation");
quote! {
#features
impl<#constraints> #ident {
pub fn get(&self) -> ::windows::core::Result<#return_type> {
if self.Status()? == #namespace AsyncStatus::Started {
let (_waiter, signaler) = ::windows::imp::Waiter::new()?;
self.SetCompleted(&#namespace #handler::new(move |_sender, _args| {
// Safe because the waiter will only be dropped after being signaled.
unsafe { signaler.signal(); }
Ok(())
}))?;
}
self.GetResults()
}
}
#features
impl<#constraints> ::std::future::Future for #ident {
type Output = ::windows::core::Result<#return_type>;
fn poll(self: ::std::pin::Pin<&mut Self>, context: &mut ::std::task::Context) -> ::std::task::Poll<Self::Output> {
if self.Status()? == #namespace AsyncStatus::Started {
let waker = context.waker().clone();
let _ = self.SetCompleted(&#namespace #handler::new(move |_sender, _args| {
waker.wake_by_ref();
Ok(())
}));
::std::task::Poll::Pending
} else {
::std::task::Poll::Ready(self.GetResults())
}
}
}
}
}
}
pub fn interface_winrt_trait(
&self,
def: TypeDef,
generics: &[Type],
ident: &TokenStream,
constraints: &TokenStream,
_phantoms: &TokenStream,
features: &TokenStream,
) -> TokenStream {
if self
.reader
.type_def_flags(def)
.contains(TypeAttributes::WINRT)
{
let type_signature = if self.reader.type_def_kind(def) == TypeKind::Class {
let type_signature =
Literal::byte_string(self.reader.type_def_signature(def, generics).as_bytes());
quote! { ::windows::imp::ConstBuffer::from_slice(#type_signature) }
} else {
let signature = Literal::byte_string(
format!("{{{:#?}}}", self.reader.type_def_guid(def).unwrap()).as_bytes(),
);
if generics.is_empty() {
quote! { ::windows::imp::ConstBuffer::from_slice(#signature) }
} else {
let generics = generics.iter().enumerate().map(|(index, g)| {
let g = self.type_name(g);
let semi = if index != generics.len() - 1 {
Some(quote! {
.push_slice(b";")
})
} else {
None
};
quote! {
.push_other(<#g as ::windows::core::RuntimeType>::SIGNATURE)
#semi
}
});
quote! {
{
::windows::imp::ConstBuffer::new()
.push_slice(b"pinterface(")
.push_slice(#signature)
.push_slice(b";")
#(#generics)*
.push_slice(b")")
}
}
}
};
quote! {
#features
impl<#constraints> ::windows::core::RuntimeType for #ident {
const SIGNATURE: ::windows::imp::ConstBuffer = #type_signature;
}
}
} else {
quote! {}
}
}
pub fn runtime_name_trait(
&self,
def: TypeDef,
_generics: &[Type],
name: &TokenStream,
constraints: &TokenStream,
features: &TokenStream,
) -> TokenStream {
if self
.reader
.type_def_flags(def)
.contains(TypeAttributes::WINRT)
{
// TODO: this needs to use a ConstBuffer-like facility to accomodate generics
let runtime_name = format!("{}", self.reader.type_def_type_name(def));
quote! {
#features
impl<#constraints> ::windows::core::RuntimeName for #name {
const NAME: &'static str = #runtime_name;
}
}
} else {
quote! {
#features
impl ::windows::core::RuntimeName for #name {}
}
}
}
pub fn interface_trait(
&self,
def: TypeDef,
generics: &[Type],
ident: &TokenStream,
constraints: &TokenStream,
features: &TokenStream,
has_unknown_base: bool,
) -> TokenStream {
if let Some(default) = self.reader.type_def_default_interface(def) {
let default_name = self.type_name(&default);
let vtbl = self.type_vtbl_name(&default);
quote! {
#features
impl<#constraints> ::core::clone::Clone for #ident {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
#features
unsafe impl ::windows::core::Interface for #ident {
type Vtable = #vtbl;
}
#features
unsafe impl ::windows::core::ComInterface for #ident {
const IID: ::windows::core::GUID = <#default_name as ::windows::core::ComInterface>::IID;
}
}
} else {
let vtbl = self.type_def_vtbl_name(def, generics);
let guid = if generics.is_empty() {
match self.reader.type_def_guid(def) {
Some(guid) => self.guid(&guid),
None => {
quote! {
::windows::core::GUID::zeroed()
}
}
}
} else {
quote! {
::windows::core::GUID::from_signature(<Self as ::windows::core::RuntimeType>::SIGNATURE)
}
};
let phantoms = self.generic_phantoms(generics);
let mut tokens = quote! {
#features
unsafe impl<#constraints> ::windows::core::Interface for #ident {
type Vtable = #vtbl;
}
#features
impl<#constraints> ::core::clone::Clone for #ident {
fn clone(&self) -> Self {
Self(self.0.clone(), #phantoms)
}
}
};
if has_unknown_base {
tokens.combine(&quote! {
#features
unsafe impl<#constraints> ::windows::core::ComInterface for #ident {
const IID: ::windows::core::GUID = #guid;
}
});
}
tokens
}
}
pub fn interface_vtbl(
&self,
def: TypeDef,
generics: &[Type],
_ident: &TokenStream,
constraints: &TokenStream,
features: &TokenStream,
) -> TokenStream {
let vtbl = self.type_def_vtbl_name(def, generics);
let mut methods = quote! {};
let mut method_names = MethodNames::new();
method_names.add_vtable_types(self, def);
let phantoms = self.generic_named_phantoms(generics);
match self.reader.type_def_vtables(def).last() {
Some(Type::IUnknown) => {
methods.combine(&quote! { pub base__: ::windows::core::IUnknown_Vtbl, })
}
Some(Type::IInspectable) => {
methods.combine(&quote! { pub base__: ::windows::core::IInspectable_Vtbl, })
}
Some(Type::TypeDef((def, _))) => {
let vtbl = self.type_def_vtbl_name(*def, &[]);
methods.combine(&quote! { pub base__: #vtbl, });
}
_ => {}
}
for method in self.reader.type_def_methods(def) {
if self.reader.method_def_name(method) == ".ctor" {
continue;
}
let name = method_names.add(self, method);
let signature = self.reader.method_def_signature(method, generics);
let mut cfg = self.reader.signature_cfg(&signature);
let signature = self.vtbl_signature(def, generics, &signature);
cfg.add_feature(self.reader.type_def_namespace(def));
let cfg_all = self.cfg_features(&cfg);
let cfg_not = self.cfg_not_features(&cfg);
let signature = quote! { pub #name: unsafe extern "system" fn #signature, };
if cfg_all.is_empty() {
methods.combine(&signature);
} else {
methods.combine(&quote! {
#cfg_all
#signature
#cfg_not
#name: usize,
});
}
}
quote! {
#features
#[repr(C)] #[doc(hidden)] pub struct #vtbl where #constraints {
#methods
#(pub #phantoms)*
}
}
}
pub fn vtbl_signature(
&self,
def: TypeDef,
_generics: &[Type],
signature: &Signature,
) -> TokenStream {
let is_winrt = self
.reader
.type_def_flags(def)
.contains(TypeAttributes::WINRT);
let hresult = self.type_name(&Type::HRESULT);
let (trailing_return_type, return_type, udt_return_type) = if is_winrt {
if let Some(return_type) = &signature.return_type {
if let Type::WinrtArray(kind) = return_type {
let tokens = self.type_abi_name(kind);
(
quote! { result_size__: *mut u32, result__: *mut *mut #tokens },
quote! { -> #hresult },
quote! {},
)
} else {
let tokens = self.type_abi_name(return_type);
(
quote! { result__: *mut #tokens },
quote! { -> #hresult },
quote! {},
)
}
} else {
(quote! {}, quote! { -> #hresult }, quote! {})
}
} else if let Some(return_type) = &signature.return_type {
if self.reader.type_is_struct(return_type) {
let tokens = self.type_abi_name(return_type);
(quote! {}, quote! {}, quote! { result__: *mut #tokens, })
} else {
let tokens = self.type_default_name(return_type);
(quote! {}, quote! { -> #tokens }, quote! {})
}
} else {
(quote! {}, quote! {}, quote! {})
};
let params = signature.params.iter().map(|p| {
let name = self.param_name(p.def);
if is_winrt {
let abi = self.type_abi_name(&p.ty);
let abi_size_name: TokenStream =
format!("{}_array_size", self.reader.param_name(p.def)).into();
if self
.reader
.param_flags(p.def)
.contains(ParamAttributes::INPUT)
{
if p.ty.is_winrt_array() {
quote! { #abi_size_name: u32, #name: *const #abi, }
} else if p.ty.is_winrt_const_ref() {
quote! { #name: &#abi, }
} else {
quote! { #name: #abi, }
}
} else if p.ty.is_winrt_array() {
quote! { #abi_size_name: u32, #name: *mut #abi, }
} else if p.ty.is_winrt_array_ref() {
quote! { #abi_size_name: *mut u32, #name: *mut *mut #abi, }
} else {
quote! { #name: *mut #abi, }
}
} else {
match p.kind {
SignatureParamKind::ValueType => {
let abi = self.type_default_name(&p.ty);
quote! { #name: #abi, }
}
_ => {
let abi = self.type_abi_name(&p.ty);
quote! { #name: #abi, }
}
}
}
});
quote! { (this: *mut ::core::ffi::c_void, #udt_return_type #(#params)* #trailing_return_type) #return_type }
}
pub fn param_name(&self, param: Param) -> TokenStream {
// In Rust, function parameters cannot be named the same as structs. This avoids some collisions that occur in the win32 metadata.
// See Icmp6SendEcho2 for an example.
to_ident(&self.reader.param_name(param).to_lowercase())
}
pub fn return_sig(&self, signature: &Signature) -> TokenStream {
if let Some(return_type) = &signature.return_type {
let tokens = self.type_default_name(return_type);
format!(" -> {}", tokens.as_str()).into()
} else if self.reader.method_def_does_not_return(signature.def) {
" -> !".into()
} else {
" -> ()".into()
}
}
pub fn win32_args(&self, params: &[SignatureParam], kind: SignatureKind) -> TokenStream {
let mut tokens = quote! {};
for (position, param) in params.iter().enumerate() {
let new = match kind {
SignatureKind::Query(query) if query.object == position => {
quote! { &mut result__, }
}
SignatureKind::ReturnValue | SignatureKind::ResultValue
if params.len() - 1 == position =>
{
quote! { &mut result__, }
}
SignatureKind::QueryOptional(query) if query.object == position => {
quote! { result__ as *mut _ as *mut _, }
}
SignatureKind::Query(query) | SignatureKind::QueryOptional(query)
if query.guid == position =>
{
quote! { &<T as ::windows::core::ComInterface>::IID, }
}
_ => {
let name = self.param_name(param.def);
let flags = self.reader.param_flags(param.def);
match param.kind {
SignatureParamKind::ArrayFixed(_)
| SignatureParamKind::ArrayRelativeLen(_)
| SignatureParamKind::ArrayRelativeByteLen(_) => {
let map = if flags.contains(ParamAttributes::OPTIONAL) {
quote! { #name.as_deref().map_or(::core::ptr::null(), |slice|slice.as_ptr()) }
} else {
quote! { #name.as_ptr() }
};
quote! { ::core::mem::transmute(#map), }
}
SignatureParamKind::ArrayRelativePtr(relative) => {
let name = self.param_name(params[relative].def);
let flags = self.reader.param_flags(params[relative].def);
if flags.contains(ParamAttributes::OPTIONAL) {
quote! { #name.as_deref().map_or(0, |slice|slice.len() as _), }
} else {
quote! { #name.len() as _, }
}
}
SignatureParamKind::TryInto => {
quote! { #name.try_into_param()?.abi(), }
}
SignatureParamKind::IntoParam => {
quote! { #name.into_param().abi(), }
}
SignatureParamKind::OptionalPointer => {
if flags.contains(ParamAttributes::OUTPUT) {
quote! { ::core::mem::transmute(#name.unwrap_or(::std::ptr::null_mut())), }
} else {
quote! { ::core::mem::transmute(#name.unwrap_or(::std::ptr::null())), }
}
}
SignatureParamKind::ValueType => {
quote! { #name, }
}
SignatureParamKind::Blittable => {
quote! { ::core::mem::transmute(#name), }
}
SignatureParamKind::Other => {
quote! { ::core::mem::transmute_copy(#name), }
}
}
}
};
tokens.combine(&new)
}
tokens
}
pub fn win32_params(&self, params: &[SignatureParam], kind: SignatureKind) -> TokenStream {
let mut tokens = quote! {};
let mut generic_params = self.generic_params(params);
for (position, param) in params.iter().enumerate() {
match kind {
SignatureKind::Query(query) | SignatureKind::QueryOptional(query) => {
if query.object == position || query.guid == position {
continue;
}
}
SignatureKind::ReturnValue | SignatureKind::ResultValue
if params.len() - 1 == position =>
{
continue;
}
_ => {}
}
let name = self.param_name(param.def);
match param.kind {
SignatureParamKind::ArrayFixed(fixed) => {
let ty = param.ty.deref();
let ty = self.type_default_name(&ty);
let len = Literal::u32_unsuffixed(fixed as _);
let ty = if self
.reader
.param_flags(param.def)
.contains(ParamAttributes::OUTPUT)
{
quote! { &mut [#ty; #len] }
} else {
quote! { &[#ty; #len] }
};
if self
.reader
.param_flags(param.def)
.contains(ParamAttributes::OPTIONAL)
{
tokens.combine(&quote! { #name: ::core::option::Option<#ty>, });
} else {
tokens.combine(&quote! { #name: #ty, });
}
}
SignatureParamKind::ArrayRelativeLen(_) => {
let ty = param.ty.deref();
let ty = self.type_default_name(&ty);
let ty = if self
.reader
.param_flags(param.def)
.contains(ParamAttributes::OUTPUT)
{
quote! { &mut [#ty] }
} else {
quote! { &[#ty] }
};
if self
.reader
.param_flags(param.def)
.contains(ParamAttributes::OPTIONAL)
{
tokens.combine(&quote! { #name: ::core::option::Option<#ty>, });
} else {
tokens.combine(&quote! { #name: #ty, });
}
}
SignatureParamKind::ArrayRelativeByteLen(_) => {
let ty = if self
.reader
.param_flags(param.def)
.contains(ParamAttributes::OUTPUT)
{
quote! { &mut [u8] }
} else {
quote! { &[u8] }
};
if self
.reader
.param_flags(param.def)
.contains(ParamAttributes::OPTIONAL)
{
tokens.combine(&quote! { #name: ::core::option::Option<#ty>, });
} else {
tokens.combine(&quote! { #name: #ty, });
}
}
SignatureParamKind::ArrayRelativePtr(_) => {}
SignatureParamKind::TryInto | SignatureParamKind::IntoParam => {
let (position, _) = generic_params.next().unwrap();
let kind: TokenStream = format!("P{position}").into();
tokens.combine(&quote! { #name: #kind, });
}
SignatureParamKind::OptionalPointer => {
let kind = self.type_default_name(&param.ty);
tokens.combine(&quote! { #name: ::core::option::Option<#kind>, });
}
SignatureParamKind::ValueType | SignatureParamKind::Blittable => {
let kind = self.type_default_name(&param.ty);
tokens.combine(&quote! { #name: #kind, });
}
SignatureParamKind::Other => {
let kind = self.type_default_name(&param.ty);
tokens.combine(&quote! { #name: &#kind, });
}
}
}
tokens
}
pub fn impl_signature(&self, def: TypeDef, signature: &Signature) -> TokenStream {
if self
.reader
.type_def_flags(def)
.contains(TypeAttributes::WINRT)
{
let is_delegate = self.reader.type_def_kind(def) == TypeKind::Delegate;
let params = signature
.params
.iter()
.map(|p| self.winrt_produce_type(p, !is_delegate));
let return_type = if let Some(return_type) = &signature.return_type {
let tokens = self.type_name(return_type);
if return_type.is_winrt_array() {
quote! { ::windows::core::Array<#tokens> }
} else {
tokens
}
} else {
quote! { () }
};
let this = if is_delegate {
quote! {}
} else {
quote! { &self, }
};
quote! { (#this #(#params),*) -> ::windows::core::Result<#return_type> }
} else {
let signature_kind = self.reader.signature_kind(signature);
let mut params = quote! {};
if signature_kind == SignatureKind::ResultValue {
for param in &signature.params[..signature.params.len() - 1] {
params.combine(&self.win32_produce_type(param));
}
} else {
for param in &signature.params {
params.combine(&self.win32_produce_type(param));
}
}
let return_type = match signature_kind {
SignatureKind::ReturnVoid => quote! {},
SignatureKind::Query(_)
| SignatureKind::QueryOptional(_)
| SignatureKind::ResultVoid => quote! { -> ::windows::core::Result<()> },
SignatureKind::ResultValue => {
let return_type = signature.params[signature.params.len() - 1].ty.deref();
let return_type = self.type_name(&return_type);
quote! { -> ::windows::core::Result<#return_type> }
}
_ => self.return_sig(signature),
};
quote! { (&self, #params) #return_type }
}
}
fn winrt_produce_type(&self, param: &SignatureParam, include_param_names: bool) -> TokenStream {
let default_type = self.type_default_name(&param.ty);
let sig = if self
.reader
.param_flags(param.def)
.contains(ParamAttributes::INPUT)
{
if param.ty.is_winrt_array() {
quote! { &[#default_type] }
} else if self.reader.type_is_primitive(&param.ty) {
quote! { #default_type }
} else if self.reader.type_is_nullable(&param.ty) {
let type_name = self.type_name(&param.ty);
quote! { ::core::option::Option<&#type_name> }
} else {
quote! { &#default_type }
}
} else if param.ty.is_winrt_array() {
quote! { &mut [#default_type] }
} else if param.ty.is_winrt_array_ref() {
let kind = self.type_name(&param.ty);
quote! { &mut ::windows::core::Array<#kind> }
} else {
quote! { &mut #default_type }
};
if include_param_names {
let name = self.param_name(param.def);
quote! { #name: #sig }
} else {
sig
}
}
fn win32_produce_type(&self, param: &SignatureParam) -> TokenStream {
let name = self.param_name(param.def);
let kind = self.type_default_name(&param.ty);
if self
.reader
.param_flags(param.def)
.contains(ParamAttributes::INPUT)
{
if self.reader.type_is_primitive(&param.ty) {
quote! { #name: #kind, }
} else if self.reader.type_is_nullable(&param.ty) {
let kind = self.type_name(&param.ty);
quote! { #name: ::core::option::Option<&#kind>, }
} else {
quote! { #name: &#kind, }
}
} else {
quote! { #name: #kind, }
}
}
}
pub fn to_ident(name: &str) -> TokenStream {
// keywords list based on https://doc.rust-lang.org/reference/keywords.html
match name {
"abstract" | "as" | "become" | "box" | "break" | "const" | "continue" | "crate" | "do"
| "else" | "enum" | "extern" | "false" | "final" | "fn" | "for" | "if" | "impl" | "in"
| "let" | "loop" | "macro" | "match" | "mod" | "move" | "mut" | "override" | "priv"
| "pub" | "ref" | "return" | "static" | "struct" | "super" | "trait" | "true" | "type"
| "typeof" | "unsafe" | "unsized" | "use" | "virtual" | "where" | "while" | "yield"
| "try" | "async" | "await" | "dyn" => format!("r#{name}").into(),
"Self" | "self" => format!("{name}_").into(),
"_" => "unused".into(),
_ => trim_tick(name).into(),
}
}
fn mut_ptrs(pointers: usize) -> TokenStream {
"*mut ".repeat(pointers).into()
}
fn const_ptrs(pointers: usize) -> TokenStream {
"*const ".repeat(pointers).into()
}
fn to_feature(name: &str) -> String {
let mut feature = String::new();
for name in name.split('.').skip(1) {
feature.push_str(name);
feature.push('_');
}
if feature.is_empty() {
feature = name.to_string();
} else {
feature.truncate(feature.len() - 1);
}
feature
}
fn starts_with(namespace: &str, feature: &str) -> bool {
if namespace == feature {
return true;
}
if namespace.len() > feature.len() && namespace.as_bytes().get(feature.len()) == Some(&b'.') {
return namespace.starts_with(feature);
}
false
}
fn gen_mut_ptrs(pointers: usize) -> TokenStream {
"*mut ".repeat(pointers).into()
}
fn gen_const_ptrs(pointers: usize) -> TokenStream {
"*const ".repeat(pointers).into()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_starts_with() {
assert!(starts_with(
"Windows.Win32.Graphics.Direct3D11on12",
"Windows.Win32.Graphics.Direct3D11on12"
));
assert!(starts_with(
"Windows.Win32.Graphics.Direct3D11on12",
"Windows.Win32.Graphics"
));
assert!(!starts_with(
"Windows.Win32.Graphics.Direct3D11on12",
"Windows.Win32.Graphics.Direct3D11"
));
assert!(!starts_with(
"Windows.Win32.Graphics.Direct3D",
"Windows.Win32.Graphics.Direct3D11"
));
}
}