use super::*;
#[derive(Clone)]
pub struct Writer {
pub reader: &'static Reader,
pub output: String,
pub namespace: &'static str,
pub implement: bool, pub std: bool, pub sys: bool, pub flatten: bool, pub package: bool, pub minimal: bool, pub no_inner_attributes: bool, }
impl Writer {
pub fn new(reader: &'static Reader, output: &str) -> Self {
Self {
reader,
output: output.to_string(),
namespace: "",
implement: false,
std: false,
sys: false,
flatten: false,
package: false,
minimal: false,
no_inner_attributes: false,
}
}
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 = def.type_name();
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),*> }
}
}
}
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 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) => generic.name().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::ConstRef(ty) => self.type_name(ty),
Type::PrimitiveOrEnum(_, ty) => self.type_name(ty),
rest => unimplemented!("{rest:?}"),
}
}
pub fn type_vtbl_name(&self, ty: &Type) -> TokenStream {
match ty {
Type::TypeDef(def, generics) => self.type_def_vtbl_name(*def, generics),
rest => unimplemented!("{rest:?}"),
}
}
pub fn type_abi_name(&self, ty: &Type) -> TokenStream {
if self.sys {
return match ty {
Type::PrimitiveOrEnum(ty, _) => self.type_default_name(ty),
_ => 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(generic.name());
quote! { ::windows_core::AbiType<#name> }
}
Type::TypeDef(def, _) => match def.kind() {
TypeKind::Enum => self.type_def_name(*def, &[]),
TypeKind::Struct => {
let tokens = self.type_def_name(*def, &[]);
if type_def_is_blittable(*def) {
tokens
} else {
quote! { ::std::mem::MaybeUninit<#tokens> }
}
}
TypeKind::Delegate => {
if def.flags().contains(TypeAttributes::WindowsRuntime) {
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),
Type::PrimitiveOrEnum(ty, _) => self.type_name(ty),
_ => self.type_name(ty),
}
}
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("e! { ::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("e! { #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("e! { #generic, });
}
tokens
}
pub fn generic_params<'b>(&'b self, params: &'b [SignatureParam]) -> impl Iterator<Item = (usize, &SignatureParam)> + 'b {
params.iter().filter(move |param| param.is_convertible()).enumerate()
}
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()
}
}
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(¶m.ty);
tokens.combine("e! { #name: ::windows_core::TryIntoParam<#into>, });
}
SignatureParamKind::IntoParam => {
let name: TokenStream = gen_name(position);
let into = self.type_name(¶m.ty);
tokens.combine("e! { #name: ::windows_core::IntoParam<#into>, });
}
_ => {}
}
}
tokens
}
pub(crate) fn cfg_doc(&self, cfg: &Cfg) -> TokenStream {
if !self.package {
quote! {}
} else {
let mut tokens = String::new();
for features in self.cfg_features_imp(cfg, self.namespace) {
write!(tokens, r#"`\"{}\"`, "#, to_feature(features)).unwrap();
}
if tokens.is_empty() {
TokenStream::new()
} else {
tokens.truncate(tokens.len() - 2);
format!(r#" #[doc = "Required features: {tokens}"]"#).into()
}
}
}
pub(crate) fn cfg_method_doc(&self, cfg: &Cfg) -> TokenStream {
if !self.package {
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: &Cfg, namespace: &str) -> Vec<&'static str> {
let mut compact = Vec::<&'static str>::new();
if self.package {
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),* )))] }
}
}
}
}
pub(crate) fn namespace(&self, namespace: &str) -> TokenStream {
if self.flatten || 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.sys {
if self.flatten {
TokenStream::new()
} else {
"::windows_sys::core::".into()
}
} else {
"::windows_core::".into()
}
}
fn scoped_name(&self, def: TypeDef) -> String {
if let Some(enclosing_type) = def.enclosing_type() {
for (index, nested_type) in self.reader.nested_types(enclosing_type).enumerate() {
if nested_type.name() == def.name() {
return format!("{}_{index}", &self.scoped_name(enclosing_type));
}
}
}
def.name().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()
}
rest => unimplemented!("{rest:?}"),
}
}
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 }
}
rest => unimplemented!("{rest:?}"),
}
}
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 agile(&self, def: TypeDef, ident: &TokenStream, constraints: &TokenStream, features: &TokenStream) -> TokenStream {
if 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 = type_def_async_kind(def);
let mut async_generics = generics.to_vec();
if kind == AsyncKind::None {
for interface in def.interface_impls().map(move |imp| imp.ty(generics)) {
if let Type::TypeDef(interface_def, interface_generics) = &interface {
kind = 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 }
}
rest => unimplemented!("{rest:?}"),
};
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_core::imp::Waiter::new()?;
self.SetCompleted(&#namespace #handler::new(move |_sender, _args| {
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 def.flags().contains(TypeAttributes::WindowsRuntime) {
let type_signature = if def.kind() == TypeKind::Class {
let type_signature = Literal::byte_string(type_def_signature(def, generics).as_bytes());
quote! { ::windows_core::imp::ConstBuffer::from_slice(#type_signature) }
} else {
let signature = Literal::byte_string(
if let Some(guid) = type_def_guid(def) { format!("{{{:#?}}}", guid) } else { "TODO".to_string() }.as_bytes(),
);
if generics.is_empty() {
quote! { ::windows_core::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_core::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_core::imp::ConstBuffer = #type_signature;
}
}
} else {
quote! {}
}
}
pub fn runtime_name_trait(&self, def: TypeDef, _generics: &[Type], name: &TokenStream, constraints: &TokenStream, features: &TokenStream) -> TokenStream {
if def.flags().contains(TypeAttributes::WindowsRuntime) {
let runtime_name = format!("{}", def.type_name());
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) = type_def_default_interface(def) {
let default_name = self.type_name(&default);
let vtbl = self.type_vtbl_name(&default);
quote! {
#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 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 mut tokens = quote! {
#features
unsafe impl<#constraints> ::windows_core::Interface for #ident {
type Vtable = #vtbl;
}
};
if has_unknown_base {
tokens.combine("e! {
#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(def);
let phantoms = self.generic_named_phantoms(generics);
match type_def_vtables(def).last() {
Some(Type::IUnknown) => methods.combine("e! { pub base__: ::windows_core::IUnknown_Vtbl, }),
Some(Type::IInspectable) => methods.combine("e! { pub base__: ::windows_core::IInspectable_Vtbl, }),
Some(Type::TypeDef(def, _)) => {
let vtbl = self.type_def_vtbl_name(*def, &[]);
methods.combine("e! { pub base__: #vtbl, });
}
_ => {}
}
for method in def.methods() {
if method.name() == ".ctor" {
continue;
}
let name = method_names.add(method);
let signature = method_def_signature(def.namespace(), method, generics);
let mut cfg = signature_cfg(method);
let signature = self.vtbl_signature(def, generics, &signature);
cfg.add_feature(def.namespace());
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("e! {
#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 = def.flags().contains(TypeAttributes::WindowsRuntime);
let crate_name = self.crate_name();
let (trailing_return_type, return_type, udt_return_type) = match &signature.return_type {
Type::Void if is_winrt => (quote! {}, quote! { -> #crate_name HRESULT }, quote! {}),
Type::Void => (quote! {}, quote! {}, quote! {}),
Type::WinrtArray(kind) => {
let tokens = self.type_abi_name(kind);
(quote! { result_size__: *mut u32, result__: *mut *mut #tokens }, quote! { -> #crate_name HRESULT }, quote! {})
}
_ if is_winrt => {
let tokens = self.type_abi_name(&signature.return_type);
(quote! { result__: *mut #tokens }, quote! { -> #crate_name HRESULT }, quote! {})
}
_ if type_is_struct(&signature.return_type) => {
let tokens = self.type_abi_name(&signature.return_type);
(quote! {}, quote! {}, quote! { result__: *mut #tokens, })
}
_ => {
let tokens = self.type_default_name(&signature.return_type);
(quote! {}, quote! { -> #tokens }, 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", p.def.name()).into();
if p.def.flags().contains(ParamAttributes::In) {
if p.ty.is_winrt_array() {
quote! { #abi_size_name: u32, #name: *const #abi, }
} else if p.ty.is_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 {
to_ident(¶m.name().to_lowercase())
}
pub fn return_sig(&self, signature: &Signature) -> TokenStream {
match &signature.return_type {
Type::Void if signature.def.has_attribute("DoesNotReturnAttribute") => " -> !".into(),
Type::Void => " -> ()".into(),
_ => {
let tokens = self.type_default_name(&signature.return_type);
format!(" -> {}", tokens.as_str()).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 = param.def.flags();
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 = params[relative].def.flags();
if flags.contains(ParamAttributes::Optional) {
quote! { #name.as_deref().map_or(0, |slice|slice.len().try_into().unwrap()), }
} else {
quote! { #name.len().try_into().unwrap(), }
}
}
SignatureParamKind::TryInto => {
quote! { #name.try_into_param()?.abi(), }
}
SignatureParamKind::IntoParam => {
quote! { #name.into_param().abi(), }
}
SignatureParamKind::OptionalPointer => {
if flags.contains(ParamAttributes::Out) {
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 => {
if matches!(param.ty, Type::PrimitiveOrEnum(_, _)) {
quote! { #name.0 as _, }
} else {
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 u32);
let ty = if param.def.flags().contains(ParamAttributes::Out) {
quote! { &mut [#ty; #len] }
} else {
quote! { &[#ty; #len] }
};
if param.def.flags().contains(ParamAttributes::Optional) {
tokens.combine("e! { #name: ::core::option::Option<#ty>, });
} else {
tokens.combine("e! { #name: #ty, });
}
}
SignatureParamKind::ArrayRelativeLen(_) => {
let ty = param.ty.deref();
let ty = self.type_default_name(&ty);
let ty = if param.def.flags().contains(ParamAttributes::Out) {
quote! { &mut [#ty] }
} else {
quote! { &[#ty] }
};
if param.def.flags().contains(ParamAttributes::Optional) {
tokens.combine("e! { #name: ::core::option::Option<#ty>, });
} else {
tokens.combine("e! { #name: #ty, });
}
}
SignatureParamKind::ArrayRelativeByteLen(_) => {
let ty = if param.def.flags().contains(ParamAttributes::Out) {
quote! { &mut [u8] }
} else {
quote! { &[u8] }
};
if param.def.flags().contains(ParamAttributes::Optional) {
tokens.combine("e! { #name: ::core::option::Option<#ty>, });
} else {
tokens.combine("e! { #name: #ty, });
}
}
SignatureParamKind::ArrayRelativePtr(_) => {}
SignatureParamKind::TryInto | SignatureParamKind::IntoParam => {
let (position, _) = generic_params.next().unwrap();
let kind: TokenStream = format!("P{position}").into();
tokens.combine("e! { #name: #kind, });
}
SignatureParamKind::OptionalPointer => {
let kind = self.type_default_name(¶m.ty);
tokens.combine("e! { #name: ::core::option::Option<#kind>, });
}
SignatureParamKind::ValueType | SignatureParamKind::Blittable => {
let kind = self.type_default_name(¶m.ty);
tokens.combine("e! { #name: #kind, });
}
SignatureParamKind::Other => {
let kind = self.type_default_name(¶m.ty);
tokens.combine("e! { #name: &#kind, });
}
}
}
tokens
}
pub fn impl_signature(&self, def: TypeDef, signature: &Signature) -> TokenStream {
if def.flags().contains(TypeAttributes::WindowsRuntime) {
let is_delegate = def.kind() == TypeKind::Delegate;
let params = signature.params.iter().map(|p| self.winrt_produce_type(p, !is_delegate));
let return_type = match &signature.return_type {
Type::Void => quote! { () },
_ => {
let tokens = self.type_name(&signature.return_type);
if signature.return_type.is_winrt_array() {
quote! { ::windows_core::Array<#tokens> }
} else {
tokens
}
}
};
let this = if is_delegate {
quote! {}
} else {
quote! { &self, }
};
quote! { (#this #(#params),*) -> ::windows_core::Result<#return_type> }
} else {
let signature_kind = signature.kind();
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(¶m.ty);
let sig = if param.def.flags().contains(ParamAttributes::In) {
if param.ty.is_winrt_array() {
quote! { &[#default_type] }
} else if type_is_primitive(¶m.ty) {
quote! { #default_type }
} else if type_is_nullable(¶m.ty) {
let type_name = self.type_name(¶m.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(¶m.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(¶m.ty);
if param.def.flags().contains(ParamAttributes::In) {
if type_is_primitive(¶m.ty) {
quote! { #name: #kind, }
} else if type_is_nullable(¶m.ty) {
let kind = self.type_name(¶m.ty);
quote! { #name: ::core::option::Option<&#kind>, }
} else {
quote! { #name: &#kind, }
}
} else {
quote! { #name: #kind, }
}
}
}
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()
}
fn type_def_async_kind(row: TypeDef) -> AsyncKind {
match row.type_name() {
TypeName::IAsyncAction => AsyncKind::Action,
TypeName::IAsyncActionWithProgress => AsyncKind::ActionWithProgress,
TypeName::IAsyncOperation => AsyncKind::Operation,
TypeName::IAsyncOperationWithProgress => AsyncKind::OperationWithProgress,
_ => AsyncKind::None,
}
}
fn type_def_is_agile(row: TypeDef) -> bool {
for attribute in row.attributes() {
match attribute.name() {
"AgileAttribute" => return true,
"MarshalingBehaviorAttribute" => {
if let Some((_, Value::EnumDef(_, value))) = attribute.args().first() {
if let Value::I32(2) = **value {
return true;
}
}
}
_ => {}
}
}
matches!(row.type_name(), TypeName::IAsyncAction | TypeName::IAsyncActionWithProgress | TypeName::IAsyncOperation | TypeName::IAsyncOperationWithProgress)
}
#[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"));
}
}