From 45df4d0d9b577fecee798d672695fe24ff57fb1b Mon Sep 17 00:00:00 2001 From: mo khan Date: Tue, 15 Jul 2025 16:37:08 -0600 Subject: feat: migrate from Cedar to SpiceDB authorization system This is a major architectural change that replaces the Cedar policy-based authorization system with SpiceDB's relation-based authorization. Key changes: - Migrate from Rust to Go implementation - Replace Cedar policies with SpiceDB schema and relationships - Switch from envoy `ext_authz` with Cedar to SpiceDB permission checks - Update build system and dependencies for Go ecosystem - Maintain Envoy integration for external authorization This change enables more flexible permission modeling through SpiceDB's Google Zanzibar inspired relation-based system, supporting complex hierarchical permissions that were difficult to express in Cedar. Breaking change: Existing Cedar policies and Rust-based configuration will no longer work and need to be migrated to SpiceDB schema. --- vendor/windows-implement/src/gen.rs | 587 ---------------------------------- vendor/windows-implement/src/lib.rs | 383 ---------------------- vendor/windows-implement/src/tests.rs | 135 -------- 3 files changed, 1105 deletions(-) delete mode 100644 vendor/windows-implement/src/gen.rs delete mode 100644 vendor/windows-implement/src/lib.rs delete mode 100644 vendor/windows-implement/src/tests.rs (limited to 'vendor/windows-implement/src') diff --git a/vendor/windows-implement/src/gen.rs b/vendor/windows-implement/src/gen.rs deleted file mode 100644 index 56253b99..00000000 --- a/vendor/windows-implement/src/gen.rs +++ /dev/null @@ -1,587 +0,0 @@ -//! Generates output for the `implement` proc macro. -//! -//! Each function in this module focuses on generating one thing, or one kind of thing. -//! Each takes `ImplementInputs` as its input. `gen_all` calls all of the `gen_*` functions -//! and merges them into the final list of output items. -//! -//! We use `parse_quote` so that we can verify that a given function generates a well-formed AST -//! item, within the narrowest possible scope. This allows us to detect errors more quickly during -//! development. If the input to `parse_quote` cannot be parsed, then the macro will panic and -//! the panic will point to the specific `parse_quote` call, rather than the entire output of the -//! `implement` proc macro being unparsable. This greatly aids in development. - -use super::*; -use quote::{quote, quote_spanned}; -use syn::{parse_quote, parse_quote_spanned}; - -/// Generates code for the `#[implements]` macro. -pub(crate) fn gen_all(inputs: &ImplementInputs) -> Vec { - let mut items: Vec = Vec::with_capacity(64); - - items.push(gen_original_impl(inputs)); - items.push(gen_impl_struct(inputs)); - items.push(gen_impl_deref(inputs)); - items.push(gen_impl_impl(inputs)); - items.push(gen_iunknown_impl(inputs)); - items.push(gen_impl_com_object_inner(inputs)); - items.extend(gen_impl_from(inputs)); - items.extend(gen_impl_com_object_interfaces(inputs)); - - for (i, interface_chain) in inputs.interface_chains.iter().enumerate() { - items.push(gen_impl_as_impl(inputs, interface_chain, i)); - } - - items -} - -/// Generates an `impl` block for the original `Foo` type. -/// -/// This `impl` block will contain `into_outer` and `into_static` (if applicable). -fn gen_original_impl(inputs: &ImplementInputs) -> syn::Item { - let original_ident = &inputs.original_ident; - let generics = &inputs.generics; - let constraints = &inputs.constraints; - - let mut output: syn::ItemImpl = parse_quote! { - impl #generics #original_ident::#generics where #constraints {} - }; - - output.items.push(gen_into_outer(inputs)); - - // Static COM objects have a lot of constraints. They can't be generic (open parameters), - // because that would be meaningless (an open generic type cannot have a known representation). - // - // Right now, we can't generate static COM objects that have base classes because we rely on - // boxing and then unboxing during construction of aggregated types. - if !inputs.is_generic { - output.items.push(gen_into_static(inputs)); - } - - syn::Item::Impl(output) -} - -/// Generates the structure definition for the `Foo_Impl` type. -fn gen_impl_struct(inputs: &ImplementInputs) -> syn::Item { - let impl_ident = &inputs.impl_ident; - let generics = &inputs.generics; - let constraints = &inputs.constraints; - let original_ident = &inputs.original_ident; - let vis = &inputs.original_type.vis; - - let mut impl_fields = quote! { - identity: &'static ::windows_core::IInspectable_Vtbl, - }; - - for interface_chain in inputs.interface_chains.iter() { - let vtbl_ty = interface_chain.implement.to_vtbl_ident(); - let chain_field_ident = &interface_chain.field_ident; - impl_fields.extend(quote! { - #chain_field_ident: &'static #vtbl_ty, - }); - } - - impl_fields.extend(quote! { - this: #original_ident::#generics, - count: ::windows_core::imp::WeakRefCount, - }); - - parse_quote! { - #[repr(C)] - #[allow(non_camel_case_types)] - #vis struct #impl_ident #generics where #constraints { - #impl_fields - } - } -} - -/// Generates the implementation of `core::ops::Deref` for the generated `Foo_Impl` type. -fn gen_impl_deref(inputs: &ImplementInputs) -> syn::Item { - let generics = &inputs.generics; - let constraints = &inputs.constraints; - let original_ident = &inputs.original_type.ident; - let impl_ident = &inputs.impl_ident; - - parse_quote! { - impl #generics ::core::ops::Deref for #impl_ident::#generics where #constraints { - type Target = #original_ident::#generics; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.this - } - } - } -} - -/// Generates an `impl` block for the generated `Foo_Impl` block. -/// -/// This generates: -/// -/// ```rust,ignore -/// const VTABLE_IDENTITY = IInspectable_Vtbl = ...; -/// const VTABLE_INTERFACE1_IFOO: IFoo_Vtbl = ...; -/// const VTABLE_INTERFACE2_IBAR: IBar_Vtbl = ...; -/// ``` -/// -/// These constants are used when constructing vtables. The benefit of using constants instead -/// of directly generating these expressions is that it allows us to overcome limitations in -/// using generics in constant contexts. Right now, Rust has a lot of limitations around using -/// constants in constant contexts. Fortunately, associated constants (constants defined within -/// `impl` blocks) work in stable Rust, even for generic types. -fn gen_impl_impl(inputs: &ImplementInputs) -> syn::Item { - let impl_ident = &inputs.impl_ident; - let generics = &inputs.generics; - let constraints = &inputs.constraints; - - let mut output: syn::ItemImpl = parse_quote! { - impl #generics #impl_ident::#generics where #constraints {} - }; - - // This is here so that IInspectable::GetRuntimeClassName can work properly. - // For a test case for this, see crates/tests/misc/component_client. - let identity_type = if let Some(first) = inputs.interface_chains.first() { - first.implement.to_ident() - } else { - quote! { ::windows_core::IInspectable } - }; - - output.items.push(parse_quote! { - const VTABLE_IDENTITY: ::windows_core::IInspectable_Vtbl = - ::windows_core::IInspectable_Vtbl::new::< - #impl_ident::#generics, - #identity_type, - 0, - >(); - }); - - for (interface_index, interface_chain) in inputs.interface_chains.iter().enumerate() { - let vtbl_ty = interface_chain.implement.to_vtbl_ident(); - let vtable_const_ident = &interface_chain.vtable_const_ident; - - let chain_offset_in_pointers: isize = -1 - interface_index as isize; - output.items.push(parse_quote! { - const #vtable_const_ident: #vtbl_ty = #vtbl_ty::new::< - #impl_ident::#generics, - #chain_offset_in_pointers, - >(); - }); - } - - syn::Item::Impl(output) -} - -/// Generates the `IUnknownImpl` implementation for the `Foo_Impl` type. -fn gen_iunknown_impl(inputs: &ImplementInputs) -> syn::Item { - let generics = &inputs.generics; - let constraints = &inputs.constraints; - let impl_ident = &inputs.impl_ident; - let original_ident = &inputs.original_type.ident; - - let trust_level = proc_macro2::Literal::usize_unsuffixed(inputs.trust_level); - - let mut output: syn::ItemImpl = parse_quote! { - impl #generics ::windows_core::IUnknownImpl for #impl_ident::#generics where #constraints { - type Impl = #original_ident::#generics; - - #[inline(always)] - fn get_impl(&self) -> &Self::Impl { - &self.this - } - - #[inline(always)] - fn get_impl_mut(&mut self) -> &mut Self::Impl { - &mut self.this - } - - #[inline(always)] - fn into_inner(self) -> Self::Impl { - self.this - } - - #[inline(always)] - fn AddRef(&self) -> u32 { - self.count.add_ref() - } - - #[inline(always)] - unsafe fn Release(self_: *mut Self) -> u32 { - let remaining = (*self_).count.release(); - if remaining == 0 { - _ = ::windows_core::imp::Box::from_raw(self_); - } - remaining - } - - #[inline(always)] - fn is_reference_count_one(&self) -> bool { - self.count.is_one() - } - - unsafe fn GetTrustLevel(&self, value: *mut i32) -> ::windows_core::HRESULT { - if value.is_null() { - return ::windows_core::imp::E_POINTER; - } - *value = #trust_level; - ::windows_core::HRESULT(0) - } - - fn to_object(&self) -> ::windows_core::ComObject { - self.count.add_ref(); - unsafe { - ::windows_core::ComObject::from_raw( - ::core::ptr::NonNull::new_unchecked(self as *const Self as *mut Self) - ) - } - } - } - }; - - let query_interface_fn = gen_query_interface(inputs); - output.items.push(syn::ImplItem::Fn(query_interface_fn)); - - syn::Item::Impl(output) -} - -/// Generates the implementation of `ComObjectInner`. -fn gen_impl_com_object_inner(inputs: &ImplementInputs) -> syn::Item { - let original_ident = &inputs.original_type.ident; - let generics = &inputs.generics; - let constraints = &inputs.constraints; - let impl_ident = &inputs.impl_ident; - - parse_quote! { - impl #generics ::windows_core::ComObjectInner for #original_ident::#generics where #constraints { - type Outer = #impl_ident::#generics; - - // IMPORTANT! This function handles assembling the "boxed" type of a COM object. - // It immediately moves the box into a heap allocation (box) and returns only a ComObject - // reference that points to it. We intentionally _do not_ expose any owned instances of - // Foo_Impl to safe Rust code, because doing so would allow unsound behavior in safe Rust - // code, due to the adjustments of the reference count that Foo_Impl permits. - // - // This is why this function returns ComObject instead of returning #impl_ident. - - fn into_object(self) -> ::windows_core::ComObject { - let boxed = ::windows_core::imp::Box::<#impl_ident::#generics>::new(self.into_outer()); - unsafe { - let ptr = ::windows_core::imp::Box::into_raw(boxed); - ::windows_core::ComObject::from_raw( - ::core::ptr::NonNull::new_unchecked(ptr) - ) - } - } - } - } -} - -/// Generates the `query_interface` method. -fn gen_query_interface(inputs: &ImplementInputs) -> syn::ImplItemFn { - let queries = inputs.interface_chains.iter().map(|interface_chain| { - let chain_ty = interface_chain.implement.to_vtbl_ident(); - let chain_field = &interface_chain.field_ident; - quote_spanned! { - interface_chain.implement.span => - if #chain_ty::matches(&iid) { - break 'found &self.#chain_field as *const _ as *const ::core::ffi::c_void; - } - } - }); - - // Dynamic casting requires that the object not contain non-static lifetimes. - let enable_dyn_casting = inputs.original_type.generics.lifetimes().count() == 0; - let dynamic_cast_query = if enable_dyn_casting { - quote! { - if iid == ::windows_core::DYNAMIC_CAST_IID { - // DYNAMIC_CAST_IID is special. We _do not_ increase the reference count for this pseudo-interface. - // Also, instead of returning an interface pointer, we simply write the `&dyn Any` directly to the - // 'interface' pointer. Since the size of `&dyn Any` is 2 pointers, not one, the caller must be - // prepared for this. This is not a normal QueryInterface call. - // - // See the `Interface::cast_to_any` method, which is the only caller that should use DYNAMIC_CAST_ID. - (interface as *mut *const dyn core::any::Any).write(self as &dyn ::core::any::Any as *const dyn ::core::any::Any); - return ::windows_core::HRESULT(0); - } - } - } else { - quote!() - }; - - let identity_query = quote! { - if iid == <::windows_core::IUnknown as ::windows_core::Interface>::IID - || iid == <::windows_core::IInspectable as ::windows_core::Interface>::IID - || iid == <::windows_core::imp::IAgileObject as ::windows_core::Interface>::IID { - break 'found &self.identity as *const _ as *const ::core::ffi::c_void; - } - }; - - let marshal_query = quote! { - #[cfg(windows)] - if iid == <::windows_core::imp::IMarshal as ::windows_core::Interface>::IID { - return ::windows_core::imp::marshaler(self.to_interface(), interface); - } - }; - - let tear_off_query = quote! { - let tear_off_ptr = self.count.query(&iid, &self.identity as *const _ as *mut _); - if !tear_off_ptr.is_null() { - *interface = tear_off_ptr; - return ::windows_core::HRESULT(0); - } - }; - - parse_quote! { - unsafe fn QueryInterface( - &self, - iid: *const ::windows_core::GUID, - interface: *mut *mut ::core::ffi::c_void, - ) -> ::windows_core::HRESULT { - unsafe { - if iid.is_null() || interface.is_null() { - return ::windows_core::imp::E_POINTER; - } - - let iid = *iid; - - let interface_ptr: *const ::core::ffi::c_void = 'found: { - #identity_query - #(#queries)* - #marshal_query - #dynamic_cast_query - #tear_off_query - - *interface = ::core::ptr::null_mut(); - return ::windows_core::imp::E_NOINTERFACE; - }; - - debug_assert!(!interface_ptr.is_null()); - *interface = interface_ptr as *mut ::core::ffi::c_void; - self.count.add_ref(); - return ::windows_core::HRESULT(0); - } - } - } -} - -/// Generates the `T::into_outer` function. This function is part of how we construct a -/// `ComObject` from a `T`. -fn gen_into_outer(inputs: &ImplementInputs) -> syn::ImplItem { - let generics = &inputs.generics; - let impl_ident = &inputs.impl_ident; - - let mut initializers = quote! { - identity: &#impl_ident::#generics::VTABLE_IDENTITY, - }; - - for interface_chain in inputs.interface_chains.iter() { - let vtbl_field_ident = &interface_chain.field_ident; - let vtable_const_ident = &interface_chain.vtable_const_ident; - - initializers.extend(quote_spanned! { - interface_chain.implement.span => - #vtbl_field_ident: &#impl_ident::#generics::#vtable_const_ident, - }); - } - - // If the type is generic then into_outer() cannot be a const fn. - let maybe_const = if inputs.is_generic { - quote!() - } else { - quote!(const) - }; - - parse_quote! { - // This constructs an "outer" object. This should only be used by the implementation - // of the outer object, never by application code. - // - // The callers of this function (`into_static` and `into_object`) are both responsible - // for maintaining one of our invariants: Application code never has an owned instance - // of the outer (implementation) type. into_static() maintains this invariant by - // returning a wrapped StaticComObject value, which owns its contents but never gives - // application code a way to mutably access its contents. This prevents the refcount - // shearing problem. - // - // TODO: Make it impossible for app code to call this function, by placing it in a - // module and marking this as private to the module. - #[inline(always)] - #maybe_const fn into_outer(self) -> #impl_ident::#generics { - #impl_ident::#generics { - #initializers - count: ::windows_core::imp::WeakRefCount::new(), - this: self, - } - } - } -} - -/// Generates the `T::into_static` function. This function is part of how we construct a -/// `StaticComObject` from a `T`. -fn gen_into_static(inputs: &ImplementInputs) -> syn::ImplItem { - assert!(!inputs.is_generic); - parse_quote! { - /// This converts a partially-constructed COM object (in the sense that it contains - /// application state but does not yet have vtable and reference count constructed) - /// into a `StaticComObject`. This allows the COM object to be stored in static - /// (global) variables. - pub const fn into_static(self) -> ::windows_core::StaticComObject { - ::windows_core::StaticComObject::from_outer(self.into_outer()) - } - } -} - -/// Generates `From`-based conversions. -/// -/// These conversions convert from the user's type `T` to `ComObject` or to an interface -/// implemented by `T`. These conversions are shorthand for calling `ComObject::new(value)`. -/// -/// We can only generate conversions from `T` to the roots of each interface chain. We can't -/// generate `From` conversions from `T` to an interface that is inherited by an interface chain, -/// because this proc macro does not have access to any information about the inheritance chain -/// of interfaces that are referenced. -/// -/// For example: -/// -/// ```rust,ignore -/// #[implement(IFoo3)] -/// struct MyType; -/// ``` -/// -/// If `IFoo3` inherits from `IFoo2`, then this code will _not_ generate a conversion for `IFoo2`. -/// However, user code can still do this: -/// -/// ```rust,ignore -/// let ifoo2 = IFoo3::from(MyType).into(); -/// ``` -/// -/// This works because the `IFoo3` type has an `Into` impl for `IFoo2`. -fn gen_impl_from(inputs: &ImplementInputs) -> Vec { - let mut items = Vec::new(); - - let original_ident = &inputs.original_type.ident; - let generics = &inputs.generics; - let constraints = &inputs.constraints; - - items.push(parse_quote! { - impl #generics ::core::convert::From<#original_ident::#generics> for ::windows_core::IUnknown where #constraints { - #[inline(always)] - fn from(this: #original_ident::#generics) -> Self { - let com_object = ::windows_core::ComObject::new(this); - com_object.into_interface() - } - } - }); - - items.push(parse_quote! { - impl #generics ::core::convert::From<#original_ident::#generics> for ::windows_core::IInspectable where #constraints { - #[inline(always)] - fn from(this: #original_ident::#generics) -> Self { - let com_object = ::windows_core::ComObject::new(this); - com_object.into_interface() - } - } - }); - - for interface_chain in inputs.interface_chains.iter() { - let interface_ident = interface_chain.implement.to_ident(); - - items.push(parse_quote_spanned! { - interface_chain.implement.span => - impl #generics ::core::convert::From<#original_ident::#generics> for #interface_ident where #constraints { - #[inline(always)] - fn from(this: #original_ident::#generics) -> Self { - let com_object = ::windows_core::ComObject::new(this); - com_object.into_interface() - } - } - }); - } - - items -} - -/// Generates the `ComObjectInterface` implementation for each interface chain. -/// -/// Each of these `impl` blocks says "this COM object implements this COM interface". -/// It allows the `ComObject` type to do conversions from the `ComObject` to `IFoo` instances, -/// _without_ doing a `QueryInterface` call. -fn gen_impl_com_object_interfaces(inputs: &ImplementInputs) -> Vec { - let mut items = Vec::new(); - - let generics = &inputs.generics; - let constraints = &inputs.constraints; - let impl_ident = &inputs.impl_ident; - - items.push(parse_quote! { - impl #generics ::windows_core::ComObjectInterface<::windows_core::IUnknown> for #impl_ident::#generics where #constraints { - #[inline(always)] - fn as_interface_ref(&self) -> ::windows_core::InterfaceRef<'_, ::windows_core::IUnknown> { - unsafe { - let interface_ptr = &self.identity; - ::core::mem::transmute(interface_ptr) - } - } - } - }); - - items.push(parse_quote! { - impl #generics ::windows_core::ComObjectInterface<::windows_core::IInspectable> for #impl_ident::#generics where #constraints { - #[inline(always)] - fn as_interface_ref(&self) -> ::windows_core::InterfaceRef<'_, ::windows_core::IInspectable> { - unsafe { - let interface_ptr = &self.identity; - ::core::mem::transmute(interface_ptr) - } - } - } - }); - - for interface_chain in inputs.interface_chains.iter() { - let chain_field = &interface_chain.field_ident; - let interface_ident = interface_chain.implement.to_ident(); - - items.push(parse_quote_spanned! { - interface_chain.implement.span => - #[allow(clippy::needless_lifetimes)] - impl #generics ::windows_core::ComObjectInterface<#interface_ident> for #impl_ident::#generics where #constraints { - #[inline(always)] - fn as_interface_ref(&self) -> ::windows_core::InterfaceRef<'_, #interface_ident> { - unsafe { - ::core::mem::transmute(&self.#chain_field) - } - } - } - }); - } - - items -} - -/// Generates the implementation of the `AsImpl` trait for a given interface chain. -fn gen_impl_as_impl( - inputs: &ImplementInputs, - interface_chain: &InterfaceChain, - interface_chain_index: usize, -) -> syn::Item { - let generics = &inputs.generics; - let constraints = &inputs.constraints; - let interface_ident = interface_chain.implement.to_ident(); - let original_ident = &inputs.original_type.ident; - let impl_ident = &inputs.impl_ident; - - parse_quote_spanned! { - interface_chain.implement.span => - impl #generics ::windows_core::AsImpl<#original_ident::#generics> for #interface_ident where #constraints { - // SAFETY: the offset is guaranteed to be in bounds, and the implementation struct - // is guaranteed to live at least as long as `self`. - #[inline(always)] - unsafe fn as_impl_ptr(&self) -> ::core::ptr::NonNull<#original_ident::#generics> { - unsafe { - let this = ::windows_core::Interface::as_raw(self); - // Subtract away the vtable offset plus 1, for the `identity` field, to get - // to the impl struct which contains that original implementation type. - let this = (this as *mut *mut ::core::ffi::c_void).sub(1 + #interface_chain_index) as *mut #impl_ident::#generics; - ::core::ptr::NonNull::new_unchecked(::core::ptr::addr_of!((*this).this) as *const #original_ident::#generics as *mut #original_ident::#generics) - } - } - } - } -} diff --git a/vendor/windows-implement/src/lib.rs b/vendor/windows-implement/src/lib.rs deleted file mode 100644 index c50509af..00000000 --- a/vendor/windows-implement/src/lib.rs +++ /dev/null @@ -1,383 +0,0 @@ -//! Implement COM interfaces for Rust types. -//! -//! Take a look at [macro@implement] for an example. -//! -//! Learn more about Rust for Windows here: - -use quote::{quote, ToTokens}; - -mod r#gen; -use r#gen::gen_all; - -#[cfg(test)] -mod tests; - -/// Implements one or more COM interfaces. -/// -/// # 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 implement( - attributes: proc_macro::TokenStream, - type_tokens: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - implement_core(attributes.into(), type_tokens.into()).into() -} - -fn implement_core( - attributes: proc_macro2::TokenStream, - item_tokens: proc_macro2::TokenStream, -) -> proc_macro2::TokenStream { - let attributes = syn::parse2::(attributes).unwrap(); - let original_type = syn::parse2::(item_tokens).unwrap(); - - // Do a little thinking and assemble ImplementInputs. We pass ImplementInputs to - // all of our gen_* function. - let inputs = ImplementInputs { - original_ident: original_type.ident.clone(), - interface_chains: convert_implements_to_interface_chains(attributes.implement), - trust_level: attributes.trust_level, - impl_ident: quote::format_ident!("{}_Impl", &original_type.ident), - constraints: { - if let Some(where_clause) = &original_type.generics.where_clause { - where_clause.predicates.to_token_stream() - } else { - quote!() - } - }, - generics: if !original_type.generics.params.is_empty() { - let mut params = quote! {}; - original_type.generics.params.to_tokens(&mut params); - quote! { <#params> } - } else { - quote! { <> } - }, - is_generic: !original_type.generics.params.is_empty(), - original_type, - }; - - let items = gen_all(&inputs); - let mut tokens = inputs.original_type.into_token_stream(); - for item in items { - tokens.extend(item.into_token_stream()); - } - - tokens -} - -/// This provides the inputs to the `gen_*` functions, which generate the proc macro output. -struct ImplementInputs { - /// The user's type that was marked with `#[implement]`. - original_type: syn::ItemStruct, - - /// The identifier for the user's original type definition. - original_ident: syn::Ident, - - /// The list of interface chains that this type implements. - interface_chains: Vec, - - /// The "trust level", which is returned by `IInspectable::GetTrustLevel`. - trust_level: usize, - - /// The identifier of the `Foo_Impl` type. - impl_ident: syn::Ident, - - /// The list of constraints needed for this `Foo_Impl` type. - constraints: proc_macro2::TokenStream, - - /// The list of generic parameters for this `Foo_Impl` type, including `<` and `>`. - /// If there are no generics, this contains `<>`. - generics: proc_macro2::TokenStream, - - /// True if the user type has any generic parameters. - is_generic: bool, -} - -/// Describes one COM interface chain. -struct InterfaceChain { - /// The name of the field for the vtable chain, e.g. `interface4_ifoo`. - field_ident: syn::Ident, - - /// The name of the associated constant item for the vtable chain's initializer, - /// e.g. `INTERFACE4_IFOO_VTABLE`. - vtable_const_ident: syn::Ident, - - implement: ImplementType, -} - -struct ImplementType { - type_name: String, - generics: Vec, - - /// The best span for diagnostics. - span: proc_macro2::Span, -} - -impl ImplementType { - fn to_ident(&self) -> proc_macro2::TokenStream { - let type_name = syn::parse_str::(&self.type_name) - .expect("Invalid token stream"); - let generics = self.generics.iter().map(|g| g.to_ident()); - quote! { #type_name<#(#generics,)*> } - } - fn to_vtbl_ident(&self) -> proc_macro2::TokenStream { - let ident = self.to_ident(); - quote! { - <#ident as ::windows_core::Interface>::Vtable - } - } -} - -#[derive(Default)] -struct ImplementAttributes { - pub implement: Vec, - pub trust_level: usize, -} - -impl syn::parse::Parse for ImplementAttributes { - fn parse(cursor: syn::parse::ParseStream<'_>) -> syn::parse::Result { - let mut input = Self::default(); - - while !cursor.is_empty() { - input.parse_implement(cursor)?; - } - - Ok(input) - } -} - -impl ImplementAttributes { - fn parse_implement(&mut self, cursor: syn::parse::ParseStream<'_>) -> syn::parse::Result<()> { - let tree = cursor.parse::()?; - self.walk_implement(&tree, &mut String::new())?; - - if !cursor.is_empty() { - cursor.parse::()?; - } - - Ok(()) - } - - fn walk_implement( - &mut self, - tree: &UseTree2, - namespace: &mut String, - ) -> syn::parse::Result<()> { - match tree { - UseTree2::Path(input) => { - if !namespace.is_empty() { - namespace.push_str("::"); - } - - namespace.push_str(&input.ident.to_string()); - self.walk_implement(&input.tree, namespace)?; - } - UseTree2::Name(_) => { - self.implement.push(tree.to_element_type(namespace)?); - } - UseTree2::Group(input) => { - for tree in &input.items { - self.walk_implement(tree, namespace)?; - } - } - UseTree2::TrustLevel(input) => self.trust_level = *input, - } - - Ok(()) - } -} - -enum UseTree2 { - Path(UsePath2), - Name(UseName2), - Group(UseGroup2), - TrustLevel(usize), -} - -impl UseTree2 { - fn to_element_type(&self, namespace: &mut String) -> syn::parse::Result { - match self { - UseTree2::Path(input) => { - if !namespace.is_empty() { - namespace.push_str("::"); - } - - namespace.push_str(&input.ident.to_string()); - input.tree.to_element_type(namespace) - } - UseTree2::Name(input) => { - let mut type_name = input.ident.to_string(); - let span = input.ident.span(); - - if !namespace.is_empty() { - type_name = format!("{namespace}::{type_name}"); - } - - let mut generics = vec![]; - - for g in &input.generics { - generics.push(g.to_element_type(&mut String::new())?); - } - - Ok(ImplementType { - type_name, - generics, - span, - }) - } - UseTree2::Group(input) => Err(syn::parse::Error::new( - input.brace_token.span.join(), - "Syntax not supported", - )), - _ => unimplemented!(), - } - } -} - -struct UsePath2 { - pub ident: syn::Ident, - pub tree: Box, -} - -struct UseName2 { - pub ident: syn::Ident, - pub generics: Vec, -} - -struct UseGroup2 { - pub brace_token: syn::token::Brace, - pub items: syn::punctuated::Punctuated, -} - -impl syn::parse::Parse for UseTree2 { - fn parse(input: syn::parse::ParseStream<'_>) -> syn::parse::Result { - let lookahead = input.lookahead1(); - if lookahead.peek(syn::Ident) { - use syn::ext::IdentExt; - let ident = input.call(syn::Ident::parse_any)?; - if input.peek(syn::Token![::]) { - input.parse::()?; - Ok(UseTree2::Path(UsePath2 { - ident, - tree: Box::new(input.parse()?), - })) - } else if input.peek(syn::Token![=]) { - if ident != "TrustLevel" { - return Err(syn::parse::Error::new( - ident.span(), - "Unrecognized key-value pair", - )); - } - input.parse::()?; - let span = input.span(); - let value = input.call(syn::Ident::parse_any)?; - match value.to_string().as_str() { - "Partial" => Ok(UseTree2::TrustLevel(1)), - "Full" => Ok(UseTree2::TrustLevel(2)), - _ => Err(syn::parse::Error::new( - span, - "`TrustLevel` must be `Partial` or `Full`", - )), - } - } else { - let generics = if input.peek(syn::Token![<]) { - input.parse::()?; - let mut generics = Vec::new(); - loop { - generics.push(input.parse::()?); - - if input.parse::().is_err() { - break; - } - } - input.parse::]>()?; - generics - } else { - Vec::new() - }; - - Ok(UseTree2::Name(UseName2 { ident, generics })) - } - } else if lookahead.peek(syn::token::Brace) { - let content; - let brace_token = syn::braced!(content in input); - let items = content.parse_terminated(UseTree2::parse, syn::Token![,])?; - - Ok(UseTree2::Group(UseGroup2 { brace_token, items })) - } else { - Err(lookahead.error()) - } - } -} - -fn convert_implements_to_interface_chains(implements: Vec) -> Vec { - let mut chains = Vec::with_capacity(implements.len()); - - for (i, implement) in implements.into_iter().enumerate() { - // Create an identifier for this interface chain. - // We only use this for naming fields; it is never visible to the developer. - // This helps with debugging. - // - // We use i + 1 so that it matches the numbering of our interface offsets. Interface 0 - // is the "identity" interface. - - let mut ident_string = format!("interface{}", i + 1); - - let suffix = get_interface_ident_suffix(&implement.type_name); - if !suffix.is_empty() { - ident_string.push('_'); - ident_string.push_str(&suffix); - } - let field_ident = syn::Ident::new(&ident_string, implement.span); - - let mut vtable_const_string = ident_string.clone(); - vtable_const_string.make_ascii_uppercase(); - vtable_const_string.insert_str(0, "VTABLE_"); - let vtable_const_ident = syn::Ident::new(&vtable_const_string, implement.span); - - chains.push(InterfaceChain { - implement, - field_ident, - vtable_const_ident, - }); - } - - chains -} - -fn get_interface_ident_suffix(type_name: &str) -> String { - let mut suffix = String::new(); - for c in type_name.chars() { - let c = c.to_ascii_lowercase(); - - if suffix.len() >= 20 { - break; - } - - if c.is_ascii_alphanumeric() { - suffix.push(c); - } - } - - suffix -} diff --git a/vendor/windows-implement/src/tests.rs b/vendor/windows-implement/src/tests.rs deleted file mode 100644 index 3f5c3493..00000000 --- a/vendor/windows-implement/src/tests.rs +++ /dev/null @@ -1,135 +0,0 @@ -//! These tests are just a way to quickly run the `#[implement]` macro and see its output. -//! They don't check the output in any way. -//! -//! This exists because of some difficulties of running `cargo expand` against the `#[implement]` -//! macro. It's also just really convenient. You can see the output by using `--nocapture` and -//! you'll probably want to restrict the output to a single thread: -//! -//! ```text -//! cargo test -p windows-implement --lib -- --nocapture --test-threads=1 -//! ``` - -use std::io::{Read, Write}; -use std::process::{Command, Stdio}; - -use proc_macro2::TokenStream; -use quote::quote; - -fn implement(attributes: TokenStream, item_tokens: TokenStream) -> String { - let out_tokens = crate::implement_core(attributes, item_tokens); - let tokens_string = out_tokens.to_string(); - - let out_string = rustfmt(&tokens_string); - println!("// output of #[implement] :"); - println!(); - println!("{}", out_string); - out_string -} - -fn rustfmt(input: &str) -> String { - let mut rustfmt = Command::new("rustfmt"); - - rustfmt.stdin(Stdio::piped()); - rustfmt.stdout(Stdio::piped()); - rustfmt.stderr(Stdio::inherit()); - - let mut child = match rustfmt.spawn() { - Ok(c) => c, - Err(e) => { - eprintln!("failed to spawn rustfmt: {e:?}"); - return input.to_string(); - } - }; - - let mut stdout = child.stdout.take().unwrap(); - - // spawn thread to read stdout - let stdout_thread = std::thread::spawn(move || { - let mut buf = String::new(); - stdout.read_to_string(&mut buf).unwrap(); - buf - }); - - // write unformatted into stdin - let mut stdin = child.stdin.take().unwrap(); - stdin.write_all(input.as_bytes()).unwrap(); - drop(stdin); - - let stdout_string: String = stdout_thread.join().unwrap(); - - let exit = child.wait().unwrap(); - if !exit.success() { - eprintln!("rustfmt terminated with failure status code"); - return input.to_string(); - } - - stdout_string -} - -#[test] -fn simple_type() { - implement( - quote!(IFoo), - quote! { - struct Foo { - x: u32, - } - }, - ); -} - -#[test] -fn zero_sized_type() { - implement( - quote!(IFoo), - quote! { - struct Foo; - }, - ); -} - -#[test] -fn no_interfaces() { - implement( - quote!(), - quote! { - struct Foo {} - }, - ); -} - -#[test] -fn generic_no_lifetime() { - implement( - quote!(IAsyncOperationWithProgress, IAsyncInfo), - quote! { - struct OperationWithProgress(SyncState>) - where - T: RuntimeType + 'static, - P: RuntimeType + 'static; - - }, - ); -} - -#[test] -fn generic_with_lifetime() { - implement( - quote!(), - quote! { - pub struct Foo<'a> { - pub x: &'a [u8], - } - }, - ); -} - -#[test] -fn tuple_type() { - implement( - quote!(IFoo), - quote! { - struct Foo(pub i32); - }, - ); -} -- cgit v1.2.3