diff options
Diffstat (limited to 'vendor/windows-interface')
| -rw-r--r-- | vendor/windows-interface/.cargo-checksum.json | 1 | ||||
| -rw-r--r-- | vendor/windows-interface/Cargo.lock | 47 | ||||
| -rw-r--r-- | vendor/windows-interface/Cargo.toml | 70 | ||||
| -rw-r--r-- | vendor/windows-interface/license-apache-2.0 | 201 | ||||
| -rw-r--r-- | vendor/windows-interface/license-mit | 21 | ||||
| -rw-r--r-- | vendor/windows-interface/src/lib.rs | 762 |
6 files changed, 1102 insertions, 0 deletions
diff --git a/vendor/windows-interface/.cargo-checksum.json b/vendor/windows-interface/.cargo-checksum.json new file mode 100644 index 00000000..8d2fc7fc --- /dev/null +++ b/vendor/windows-interface/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.lock":"71976eebe87246b69143413e859eaab13a93c42023ab2769de31c285e583bee6","Cargo.toml":"744f3033320d71ddbd8c260605dc161b856e29abcf4a7ea8751f2d5ffb1df75e","license-apache-2.0":"c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b","license-mit":"c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383","src/lib.rs":"f94cff0cfbb191170f6333c858e78eaaf1c51aae7f9b86b5cbb64c0d93d3bae3"},"package":"bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"}
\ No newline at end of file diff --git a/vendor/windows-interface/Cargo.lock b/vendor/windows-interface/Cargo.lock new file mode 100644 index 00000000..9f8d116b --- /dev/null +++ b/vendor/windows-interface/Cargo.lock @@ -0,0 +1,47 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "windows-interface" +version = "0.59.1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/vendor/windows-interface/Cargo.toml b/vendor/windows-interface/Cargo.toml new file mode 100644 index 00000000..997ef698 --- /dev/null +++ b/vendor/windows-interface/Cargo.toml @@ -0,0 +1,70 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.74" +name = "windows-interface" +version = "0.59.1" +authors = ["Microsoft"] +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "The interface macro for the windows crate" +readme = false +license = "MIT OR Apache-2.0" +repository = "https://github.com/microsoft/windows-rs" + +[package.metadata.docs.rs] +default-target = "x86_64-pc-windows-msvc" +targets = [] + +[lib] +name = "windows_interface" +path = "src/lib.rs" +proc-macro = true + +[dependencies.proc-macro2] +version = "1.0" +default-features = false + +[dependencies.quote] +version = "1.0" +default-features = false + +[dependencies.syn] +version = "2.0" +features = [ + "parsing", + "proc-macro", + "printing", + "full", + "clone-impls", +] +default-features = false + +[dev-dependencies] + +[lints.rust] +missing_docs = "warn" +unsafe_op_in_unsafe_fn = "warn" + +[lints.rust.rust_2018_idioms] +level = "warn" +priority = -1 + +[lints.rust.unexpected_cfgs] +level = "warn" +priority = 0 +check-cfg = ["cfg(windows_raw_dylib, windows_debugger_visualizer, windows_slim_errors)"] diff --git a/vendor/windows-interface/license-apache-2.0 b/vendor/windows-interface/license-apache-2.0 new file mode 100644 index 00000000..b5ed4ece --- /dev/null +++ b/vendor/windows-interface/license-apache-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/windows-interface/license-mit b/vendor/windows-interface/license-mit new file mode 100644 index 00000000..9e841e7a --- /dev/null +++ b/vendor/windows-interface/license-mit @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/vendor/windows-interface/src/lib.rs b/vendor/windows-interface/src/lib.rs new file mode 100644 index 00000000..73c8f40c --- /dev/null +++ b/vendor/windows-interface/src/lib.rs @@ -0,0 +1,762 @@ +//! Define COM interfaces to call or implement. +//! +//! Take a look at [macro@interface] for an example. +//! +//! Learn more about Rust for Windows here: <https://github.com/microsoft/windows-rs> + +use quote::quote; +use syn::spanned::Spanned; + +/// Defines a COM interface to call or implement. +/// +/// # Example +/// ```rust,no_run +/// use windows_core::*; +/// +/// #[interface("094d70d6-5202-44b8-abb8-43860da5aca2")] +/// unsafe trait IValue: IUnknown { +/// fn GetValue(&self, value: *mut i32) -> HRESULT; +/// } +/// +/// #[implement(IValue)] +/// struct Value(i32); +/// +/// impl IValue_Impl for Value_Impl { +/// unsafe fn GetValue(&self, value: *mut i32) -> HRESULT { +/// *value = self.0; +/// HRESULT(0) +/// } +/// } +/// +/// let object: IValue = Value(123).into(); +/// // Call interface methods... +/// ``` +#[proc_macro_attribute] +pub fn interface( + attributes: proc_macro::TokenStream, + original_type: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let guid = syn::parse_macro_input!(attributes as Guid); + let interface = syn::parse_macro_input!(original_type as Interface); + let tokens = match interface.gen_tokens(&guid) { + Ok(t) => t, + Err(e) => return e.to_compile_error().into(), + }; + tokens.into() +} + +macro_rules! bail { + ($item:expr, $($msg:tt),*) => { + return Err(syn::Error::new($item.span(), std::fmt::format(format_args!($($msg),*)))); + }; + +} + +macro_rules! unexpected_token { + ($item:expr, $msg:expr) => { + if let Some(i) = $item { + bail!(i, "unexpected {}", $msg); + } + }; +} +macro_rules! expected_token { + ($sig:tt.$item:tt(), $msg:expr) => { + if let None = $sig.$item() { + bail!($sig, "expected {}", $msg); + } + }; +} + +/// Parsed interface +/// +/// ```rust,ignore +/// #[windows_interface::interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")] +/// unsafe trait IUIAnimationVariable: IUnknown { +/// //^ parses this +/// fn GetValue(&self, value: *mut f64) -> HRESULT; +/// } +/// ``` +struct Interface { + visibility: syn::Visibility, + name: syn::Ident, + parent: Option<syn::Path>, + methods: Vec<InterfaceMethod>, + docs: Vec<syn::Attribute>, +} + +impl Interface { + /// Generates all the code needed for a COM interface + fn gen_tokens(&self, guid: &Guid) -> syn::Result<proc_macro2::TokenStream> { + let vis = &self.visibility; + let name = &self.name; + let docs = &self.docs; + let parent = self.parent_type(); + let vtable_name = quote::format_ident!("{}_Vtbl", name); + let guid = guid.to_tokens()?; + let implementation = self.gen_implementation(); + let com_trait = self.get_com_trait(); + let vtable = self.gen_vtable(&vtable_name); + let conversions = self.gen_conversions(); + + Ok(quote! { + #[repr(transparent)] + #(#docs)* + #vis struct #name(#parent); + #implementation + unsafe impl ::windows_core::Interface for #name { + type Vtable = #vtable_name; + const IID: ::windows_core::GUID = #guid; + } + impl ::windows_core::RuntimeName for #name {} + impl ::core::ops::Deref for #name { + type Target = #parent; + fn deref(&self) -> &Self::Target { + unsafe { ::core::mem::transmute(self) } + } + } + #com_trait + #vtable + #conversions + }) + } + + /// Generates the methods users can call on the COM interface pointer + fn gen_implementation(&self) -> proc_macro2::TokenStream { + let name = &self.name; + let methods = self + .methods + .iter() + .map(|m| { + let vis = &m.visibility; + let name = &m.name; + + let generics = m.gen_consume_generics(); + let params = m.gen_consume_params(); + let args = m.gen_consume_args(); + let ret = &m.ret; + + if m.is_result() { + quote! { + #[inline(always)] + #vis unsafe fn #name<#(#generics),*>(&self, #(#params),*) #ret { + (::windows_core::Interface::vtable(self).#name)(::windows_core::Interface::as_raw(self), #(#args),*).ok() + } + } + } else { + quote! { + #[inline(always)] + #vis unsafe fn #name<#(#generics),*>(&self, #(#params),*) #ret { + (::windows_core::Interface::vtable(self).#name)(::windows_core::Interface::as_raw(self), #(#args),*) + } + } + } + }) + .collect::<Vec<_>>(); + quote! { + impl #name { + #(#methods)* + } + } + } + + fn get_com_trait(&self) -> proc_macro2::TokenStream { + let name = quote::format_ident!("{}_Impl", self.name); + let vis = &self.visibility; + let methods = self + .methods + .iter() + .map(|m| { + let name = &m.name; + let docs = &m.docs; + let args = m.gen_args(); + let ret = &m.ret; + quote! { + #(#docs)* + unsafe fn #name(&self, #(#args),*) #ret; + } + }) + .collect::<Vec<_>>(); + let parent = self.parent_trait_constraint(); + + quote! { + #[allow(non_camel_case_types)] + #vis trait #name: Sized + #parent { + #(#methods)* + } + } + } + + /// Generates the vtable for a COM interface + fn gen_vtable(&self, vtable_name: &syn::Ident) -> proc_macro2::TokenStream { + let vis = &self.visibility; + let name = &self.name; + let trait_name = quote::format_ident!("{}_Impl", name); + let implvtbl_name = quote::format_ident!("{}_ImplVtbl", name); + + let vtable_entries = self + .methods + .iter() + .map(|m| { + let name = &m.name; + let ret = &m.ret; + let args = m.gen_args(); + + if m.is_result() { + quote! { + pub #name: unsafe extern "system" fn(this: *mut ::core::ffi::c_void, #(#args),*) -> ::windows_core::HRESULT, + } + } else { + quote! { + pub #name: unsafe extern "system" fn(this: *mut ::core::ffi::c_void, #(#args),*) #ret, + } + } + }) + .collect::<Vec<_>>(); + + let parent_vtable_generics = quote!(Identity, OFFSET); + let parent_vtable = self.parent_vtable(); + + // or_parent_matches will be `|| parent::matches(iid)` if this interface inherits from another + // interface (except for IUnknown) or will be empty if this is not applicable. This is what allows + // QueryInterface to work correctly for all interfaces in an inheritance chain, e.g. + // IFoo3 derives from IFoo2 derives from IFoo. + // + // We avoid matching IUnknown because object identity depends on the uniqueness of the IUnknown pointer. + let or_parent_matches = match parent_vtable.as_ref() { + Some(parent) if !self.parent_is_iunknown() => quote! (|| <#parent>::matches(iid)), + _ => quote!(), + }; + + let functions = self + .methods + .iter() + .map(|m| { + let name = &m.name; + let args = m.gen_args(); + let params = &m + .args + .iter() + .map(|a| { + let pat = &a.pat; + quote! { #pat } + }) + .collect::<Vec<_>>(); + let ret = &m.ret; + + let ret = if m.is_result() { + quote! { -> ::windows_core::HRESULT } + } else { + quote! { #ret } + }; + + if parent_vtable.is_some() { + quote! { + unsafe extern "system" fn #name< + Identity: ::windows_core::IUnknownImpl, + const OFFSET: isize + >( + this: *mut ::core::ffi::c_void, // <-- This is the COM "this" pointer, which is not the same as &T or &T_Impl. + #(#args),* + ) #ret + where + Identity : #trait_name + { + // This step is essentially a virtual dispatch adjustor thunk. Its purpose is to adjust + // the "this" pointer from the address used by the COM interface to the root of the + // MyApp_Impl object. Since a given MyApp_Impl may implement more than one COM interface + // (and more than one COM interface chain), we need to know how to get from COM's "this" + // back to &MyApp_Impl. The OFFSET constant gives us the value (in pointer-sized units). + let this_outer: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity); + + // Last, we invoke the implementation function. + // We use explicit <Impl as IFoo_Impl> so that we can select the correct method + // for situations where IFoo3 derives from IFoo2 and both declare a method with + // the same name. + <Identity as #trait_name>::#name(this_outer, #(#params),*).into() + } + } + } else { + quote! { + unsafe extern "system" fn #name<Impl: #trait_name>(this: *mut ::core::ffi::c_void, #(#args),*) #ret { + let this = (this as *mut *mut ::core::ffi::c_void) as *const ::windows_core::ScopedHeap; + let this = (*this).this as *const Impl; + (*this).#name(#(#params),*).into() + } + } + } + }) + .collect::<Vec<_>>(); + + if let Some(parent_vtable) = parent_vtable { + let entries = self + .methods + .iter() + .map(|m| { + let name = &m.name; + quote!(#name: #name::<Identity, OFFSET>) + }) + .collect::<Vec<_>>(); + + quote! { + #[repr(C)] + #[doc(hidden)] + #vis struct #vtable_name { + pub base__: #parent_vtable, + #(#vtable_entries)* + } + impl #vtable_name { + pub const fn new< + Identity: ::windows_core::IUnknownImpl, + const OFFSET: isize, + >() -> Self + where + Identity : #trait_name + { + #(#functions)* + Self { base__: #parent_vtable::new::<#parent_vtable_generics>(), #(#entries),* } + } + + #[inline(always)] + pub fn matches(iid: &::windows_core::GUID) -> bool { + *iid == <#name as ::windows_core::Interface>::IID + #or_parent_matches + } + } + } + } else { + let entries = self + .methods + .iter() + .map(|m| { + let name = &m.name; + quote!(#name: #name::<Impl>) + }) + .collect::<Vec<_>>(); + + quote! { + #[repr(C)] + #[doc(hidden)] + #vis struct #vtable_name { + #(#vtable_entries)* + } + impl #vtable_name { + pub const fn new<Impl: #trait_name>() -> Self { + #(#functions)* + Self { #(#entries),* } + } + } + struct #implvtbl_name<T: #trait_name> (::core::marker::PhantomData<T>); + impl<T: #trait_name> #implvtbl_name<T> { + const VTABLE: #vtable_name = #vtable_name::new::<T>(); + } + impl #name { + fn new<'a, T: #trait_name>(this: &'a T) -> ::windows_core::ScopedInterface<'a, #name> { + let this = ::windows_core::ScopedHeap { vtable: &#implvtbl_name::<T>::VTABLE as *const _ as *const _, this: this as *const _ as *const _ }; + let this = ::core::mem::ManuallyDrop::new(::windows_core::imp::Box::new(this)); + unsafe { ::windows_core::ScopedInterface::new(::core::mem::transmute(&this.vtable)) } + } + } + } + } + } + + /// Generates various conversions such as from and to `IUnknown` + fn gen_conversions(&self) -> proc_macro2::TokenStream { + let name = &self.name; + let name_string = format!("{name}"); + quote! { + impl ::core::convert::From<#name> for ::windows_core::IUnknown { + fn from(value: #name) -> Self { + unsafe { ::core::mem::transmute(value) } + } + } + impl ::core::convert::From<&#name> for ::windows_core::IUnknown { + fn from(value: &#name) -> Self { + ::core::convert::From::from(::core::clone::Clone::clone(value)) + } + } + impl ::core::clone::Clone for #name { + fn clone(&self) -> Self { + Self(self.0.clone()) + } + } + impl ::core::cmp::PartialEq for #name { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } + } + impl ::core::cmp::Eq for #name {} + impl ::core::fmt::Debug for #name { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_tuple(#name_string).field(&::windows_core::Interface::as_raw(self)).finish() + } + } + } + } + + fn parent_type(&self) -> proc_macro2::TokenStream { + if let Some(parent) = &self.parent { + quote!(#parent) + } else { + quote!(::core::ptr::NonNull<::core::ffi::c_void>) + } + } + + fn parent_vtable(&self) -> Option<proc_macro2::TokenStream> { + if let Some((ident, path)) = self.parent_path().split_last() { + let ident = quote::format_ident!("{}_Vtbl", ident); + Some(quote! { #(#path::)* #ident }) + } else { + None + } + } + + fn parent_is_iunknown(&self) -> bool { + if let Some(ident) = self.parent_path().last() { + ident == "IUnknown" + } else { + false + } + } + + fn parent_path(&self) -> Vec<syn::Ident> { + if let Some(parent) = &self.parent { + parent + .segments + .iter() + .map(|segment| segment.ident.clone()) + .collect() + } else { + vec![] + } + } + + /// Gets the parent trait constraint which is nothing if the parent is IUnknown + fn parent_trait_constraint(&self) -> proc_macro2::TokenStream { + if let Some((ident, path)) = self.parent_path().split_last() { + if ident != "IUnknown" { + let ident = quote::format_ident!("{}_Impl", ident); + return quote! { #(#path::)* #ident }; + } + } + + quote! {} + } +} + +impl syn::parse::Parse for Interface { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> { + let attributes = input.call(syn::Attribute::parse_outer)?; + let mut docs = Vec::new(); + for attr in attributes.into_iter() { + let path = attr.path(); + if path.is_ident("doc") { + docs.push(attr); + } else { + return Err(syn::Error::new(path.span(), "Unrecognized attribute ")); + } + } + + let visibility = input.parse::<syn::Visibility>()?; + _ = input.parse::<syn::Token![unsafe]>()?; + _ = input.parse::<syn::Token![trait]>()?; + let name = input.parse::<syn::Ident>()?; + _ = input.parse::<syn::Token![:]>(); + let parent = input.parse::<syn::Path>().ok(); + let content; + syn::braced!(content in input); + let mut methods = Vec::new(); + while !content.is_empty() { + methods.push(content.parse::<InterfaceMethod>()?); + } + Ok(Self { + visibility, + methods, + name, + parent, + docs, + }) + } +} + +/// Parsed interface guid attribute +/// +/// ```rust,ignore +/// #[windows_interface::interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")] +/// //^ parses this +/// unsafe trait IUIAnimationVariable: IUnknown { +/// fn GetValue(&self, value: *mut f64) -> HRESULT; +/// } +/// ``` +struct Guid(Option<syn::LitStr>); + +impl Guid { + fn to_tokens(&self) -> syn::Result<proc_macro2::TokenStream> { + fn hex_lit(num: &str) -> syn::LitInt { + syn::LitInt::new(&format!("0x{num}"), proc_macro2::Span::call_site()) + } + + fn ensure_length( + part: Option<&str>, + index: usize, + length: usize, + span: proc_macro2::Span, + ) -> syn::Result<String> { + let part = match part { + Some(p) => p, + None => { + return Err(syn::Error::new( + span, + format!("The IID missing part at index {index}"), + )) + } + }; + + if part.len() != length { + return Err(syn::Error::new( + span, + format!( + "The IID part at index {} must be {} characters long but was {} characters", + index, + length, + part.len() + ), + )); + } + + Ok(part.to_owned()) + } + + if let Some(value) = &self.0 { + let guid_value = value.value(); + let mut delimited = guid_value.split('-').fuse(); + let chunks = [ + ensure_length(delimited.next(), 0, 8, value.span())?, + ensure_length(delimited.next(), 1, 4, value.span())?, + ensure_length(delimited.next(), 2, 4, value.span())?, + ensure_length(delimited.next(), 3, 4, value.span())?, + ensure_length(delimited.next(), 4, 12, value.span())?, + ]; + + let data1 = hex_lit(&chunks[0]); + let data2 = hex_lit(&chunks[1]); + let data3 = hex_lit(&chunks[2]); + let (data4_1, data4_2) = chunks[3].split_at(2); + let data4_1 = hex_lit(data4_1); + let data4_2 = hex_lit(data4_2); + let (data4_3, rest) = chunks[4].split_at(2); + let data4_3 = hex_lit(data4_3); + + let (data4_4, rest) = rest.split_at(2); + let data4_4 = hex_lit(data4_4); + + let (data4_5, rest) = rest.split_at(2); + let data4_5 = hex_lit(data4_5); + + let (data4_6, rest) = rest.split_at(2); + let data4_6 = hex_lit(data4_6); + + let (data4_7, data4_8) = rest.split_at(2); + let data4_7 = hex_lit(data4_7); + let data4_8 = hex_lit(data4_8); + Ok(quote! { + ::windows_core::GUID { + data1: #data1, + data2: #data2, + data3: #data3, + data4: [#data4_1, #data4_2, #data4_3, #data4_4, #data4_5, #data4_6, #data4_7, #data4_8] + } + }) + } else { + Ok(quote! { + ::windows_core::GUID::zeroed() + }) + } + } +} + +impl syn::parse::Parse for Guid { + fn parse(cursor: syn::parse::ParseStream<'_>) -> syn::Result<Self> { + let string: Option<syn::LitStr> = cursor.parse().ok(); + + Ok(Self(string)) + } +} + +/// A parsed interface method +/// +/// ```rust,ignore +/// #[windows_interface::interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")] +/// unsafe trait IUIAnimationVariable: IUnknown { +/// fn GetValue(&self, value: *mut f64) -> HRESULT; +/// //^ parses this +/// } +/// ``` +struct InterfaceMethod { + pub name: syn::Ident, + pub visibility: syn::Visibility, + pub args: Vec<InterfaceMethodArg>, + pub ret: syn::ReturnType, + pub docs: Vec<syn::Attribute>, +} + +impl InterfaceMethod { + fn is_result(&self) -> bool { + if let syn::ReturnType::Type(_, ty) = &self.ret { + if let syn::Type::Path(path) = &**ty { + if let Some(segment) = path.path.segments.last() { + let ident = segment.ident.to_string(); + if ident == "Result" { + if let syn::PathArguments::AngleBracketed(args) = &segment.arguments { + if args.args.len() == 1 { + return true; + } + } + } + } + } + } + + false + } + + /// Generates arguments (of the form `$pat: $type`) + fn gen_args(&self) -> Vec<proc_macro2::TokenStream> { + self.args + .iter() + .map(|a| { + let pat = &a.pat; + let ty = &a.ty; + quote! { #pat: #ty } + }) + .collect::<Vec<_>>() + } + + fn gen_consume_generics(&self) -> Vec<proc_macro2::TokenStream> { + self.args + .iter() + .enumerate() + .filter_map(|(generic_index, a)| { + if let Some((ty, ident)) = a.borrow_type() { + let generic_ident = quote::format_ident!("P{generic_index}"); + if ident == "Ref" { + Some(quote! { #generic_ident: ::windows_core::Param<#ty> }) + } else { + Some(quote! { #generic_ident: ::windows_core::OutParam<#ty> }) + } + } else { + None + } + }) + .collect::<Vec<_>>() + } + + fn gen_consume_params(&self) -> Vec<proc_macro2::TokenStream> { + self.args + .iter() + .enumerate() + .map(|(generic_index, a)| { + let pat = &a.pat; + + if a.borrow_type().is_some() { + let generic_ident = quote::format_ident!("P{generic_index}"); + quote! { #pat: #generic_ident } + } else { + let ty = &a.ty; + quote! { #pat: #ty } + } + }) + .collect::<Vec<_>>() + } + + fn gen_consume_args(&self) -> Vec<proc_macro2::TokenStream> { + self.args + .iter() + .map(|a| { + let pat = &a.pat; + + if let Some((_, ident)) = a.borrow_type() { + if ident == "Ref" { + quote! { #pat.param().borrow() } + } else { + quote! { #pat.borrow_mut() } + } + } else { + quote! { #pat } + } + }) + .collect::<Vec<_>>() + } +} + +impl syn::parse::Parse for InterfaceMethod { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> { + let docs = input.call(syn::Attribute::parse_outer)?; + let visibility = input.parse::<syn::Visibility>()?; + let method = input.parse::<syn::TraitItemFn>()?; + unexpected_token!(docs.iter().find(|a| !a.path().is_ident("doc")), "attribute"); + unexpected_token!(method.default, "default method implementation"); + let sig = method.sig; + unexpected_token!(sig.abi, "abi declaration"); + unexpected_token!(sig.asyncness, "async declaration"); + unexpected_token!(sig.generics.params.iter().next(), "generics declaration"); + unexpected_token!(sig.constness, "const declaration"); + expected_token!( + sig.receiver(), + "the method to have &self as its first argument" + ); + unexpected_token!(sig.variadic, "variadic args"); + let args = sig + .inputs + .into_iter() + .filter_map(|a| match a { + syn::FnArg::Receiver(_) => None, + syn::FnArg::Typed(p) => Some(p), + }) + .map(|p| { + Ok(InterfaceMethodArg { + ty: p.ty, + pat: p.pat, + }) + }) + .collect::<Result<Vec<InterfaceMethodArg>, syn::Error>>()?; + + let ret = sig.output; + Ok(InterfaceMethod { + name: sig.ident, + visibility, + args, + ret, + docs, + }) + } +} + +/// An argument to an interface method +struct InterfaceMethodArg { + /// The type of the argument + pub ty: Box<syn::Type>, + /// The name of the argument + pub pat: Box<syn::Pat>, +} + +impl InterfaceMethodArg { + fn borrow_type(&self) -> Option<(syn::Type, String)> { + if let syn::Type::Path(path) = &*self.ty { + if let Some(segment) = path.path.segments.last() { + let ident = segment.ident.to_string(); + if matches!(ident.as_str(), "Ref" | "OutRef") { + if let syn::PathArguments::AngleBracketed(args) = &segment.arguments { + if args.args.len() == 1 { + if let Some(syn::GenericArgument::Type(ty)) = args.args.first() { + return Some((ty.clone(), ident)); + } + } + } + } + } + } + + None + } +} |
