Skip to main content

windows_implement/
lib.rs

1//! Implement COM interfaces for Rust types.
2//!
3//! See [`implement`] for an example.
4
5use quote::{ToTokens, quote};
6
7mod r#gen;
8use r#gen::gen_all;
9
10#[cfg(test)]
11mod tests;
12
13/// Implements one or more COM interfaces.
14///
15/// ```
16/// use windows_core::*;
17///
18/// #[interface("094d70d6-5202-44b8-abb8-43860da5aca2")]
19/// unsafe trait IValue: IUnknown {
20///     fn GetValue(&self, value: *mut i32) -> HRESULT;
21/// }
22///
23/// #[implement(IValue)]
24/// struct Value(i32);
25///
26/// impl IValue_Impl for Value_Impl {
27///     unsafe fn GetValue(&self, value: *mut i32) -> HRESULT {
28///         unsafe { *value = self.0 };
29///         HRESULT(0)
30///     }
31/// }
32///
33/// let _: IValue = Value(123).into();
34/// ```
35#[proc_macro_attribute]
36pub fn implement(
37    attributes: proc_macro::TokenStream,
38    type_tokens: proc_macro::TokenStream,
39) -> proc_macro::TokenStream {
40    implement_core(attributes.into(), type_tokens.into()).into()
41}
42
43fn implement_core(
44    attributes: proc_macro2::TokenStream,
45    item_tokens: proc_macro2::TokenStream,
46) -> proc_macro2::TokenStream {
47    let attributes = match syn::parse2::<ImplementAttributes>(attributes) {
48        Ok(a) => a,
49        Err(e) => return e.into_compile_error(),
50    };
51    let original_type = match syn::parse2::<syn::ItemStruct>(item_tokens) {
52        Ok(t) => t,
53        Err(e) => return e.into_compile_error(),
54    };
55
56    let inputs = ImplementInputs {
57        original_ident: original_type.ident.clone(),
58        interface_chains: convert_implements_to_interface_chains(attributes.implement),
59        trust_level: attributes.trust_level,
60        agile: attributes.agile,
61        impl_ident: quote::format_ident!("{}_Impl", &original_type.ident),
62        constraints: {
63            if let Some(where_clause) = &original_type.generics.where_clause {
64                where_clause.predicates.to_token_stream()
65            } else {
66                quote!()
67            }
68        },
69        generics: if !original_type.generics.params.is_empty() {
70            let mut params = quote! {};
71            original_type.generics.params.to_tokens(&mut params);
72            quote! { <#params> }
73        } else {
74            quote! { <> }
75        },
76        generics_idents: if !original_type.generics.params.is_empty() {
77            let idents: Vec<_> = original_type
78                .generics
79                .params
80                .iter()
81                .map(|param| {
82                    let mut ident = quote! {};
83                    match param {
84                        syn::GenericParam::Type(ty) => ty.ident.to_tokens(&mut ident),
85                        syn::GenericParam::Lifetime(lt) => lt.lifetime.to_tokens(&mut ident),
86                        syn::GenericParam::Const(cnst) => cnst.ident.to_tokens(&mut ident),
87                    };
88
89                    ident
90                })
91                .collect();
92            quote! { <#(#idents),*> }
93        } else {
94            quote! { <> }
95        },
96        is_generic: !original_type.generics.params.is_empty(),
97        original_type,
98    };
99
100    let items = gen_all(&inputs);
101    let mut tokens = inputs.original_type.into_token_stream();
102    for item in items {
103        tokens.extend(item.into_token_stream());
104    }
105
106    tokens
107}
108
109/// This provides the inputs to the `gen_*` functions, which generate the proc macro output.
110struct ImplementInputs {
111    /// The user's type that was marked with `#[implement]`.
112    original_type: syn::ItemStruct,
113
114    /// The identifier for the user's original type definition.
115    original_ident: syn::Ident,
116
117    /// The list of interface chains that this type implements.
118    interface_chains: Vec<InterfaceChain>,
119
120    /// The "trust level", which is returned by `IInspectable::GetTrustLevel`.
121    trust_level: usize,
122
123    /// Determines whether `IAgileObject` and `IMarshal` are implemented automatically.
124    agile: bool,
125
126    /// The identifier of the `Foo_Impl` type.
127    impl_ident: syn::Ident,
128
129    /// The list of constraints needed for this `Foo_Impl` type.
130    constraints: proc_macro2::TokenStream,
131
132    /// The list of generic parameters for this `Foo_Impl` type, including `<` and `>`.
133    /// If there are no generics, this contains `<>`.
134    generics: proc_macro2::TokenStream,
135
136    /// The list of generic parameters without any bounds, e.g. `<T, 'a, THING>`.
137    /// Used when applying the parameters to the generated type.
138    generics_idents: proc_macro2::TokenStream,
139
140    /// True if the user type has any generic parameters.
141    is_generic: bool,
142}
143
144/// Describes one COM interface chain.
145struct InterfaceChain {
146    /// The name of the field for the vtable chain, e.g. `interface4_ifoo`.
147    field_ident: syn::Ident,
148
149    /// The name of the associated constant item for the vtable chain's initializer,
150    /// e.g. `INTERFACE4_IFOO_VTABLE`.
151    vtable_const_ident: syn::Ident,
152
153    implement: ImplementType,
154}
155
156struct ImplementType {
157    type_name: String,
158    generics: Vec<Self>,
159
160    /// The best span for diagnostics.
161    span: proc_macro2::Span,
162}
163
164impl ImplementType {
165    fn to_ident(&self) -> proc_macro2::TokenStream {
166        let type_name = syn::parse_str::<proc_macro2::TokenStream>(&self.type_name)
167            .expect("Invalid token stream");
168        let generics = self.generics.iter().map(|g| g.to_ident());
169        quote! { #type_name<#(#generics,)*> }
170    }
171    fn to_vtbl_ident(&self) -> proc_macro2::TokenStream {
172        let ident = self.to_ident();
173        quote! {
174            <#ident as ::windows_core::Interface>::Vtable
175        }
176    }
177}
178
179#[derive(Default)]
180struct ImplementAttributes {
181    pub implement: Vec<ImplementType>,
182    pub trust_level: usize,
183    pub agile: bool,
184}
185
186impl syn::parse::Parse for ImplementAttributes {
187    fn parse(cursor: syn::parse::ParseStream) -> syn::parse::Result<Self> {
188        let mut input = Self {
189            agile: true,
190            ..Default::default()
191        };
192
193        while !cursor.is_empty() {
194            input.parse_implement(cursor)?;
195        }
196
197        Ok(input)
198    }
199}
200
201impl ImplementAttributes {
202    fn parse_implement(&mut self, cursor: syn::parse::ParseStream) -> syn::parse::Result<()> {
203        let tree = cursor.parse::<UseTree2>()?;
204        self.walk_implement(&tree, &mut String::new())?;
205
206        if !cursor.is_empty() {
207            cursor.parse::<syn::Token![,]>()?;
208        }
209
210        Ok(())
211    }
212
213    fn walk_implement(
214        &mut self,
215        tree: &UseTree2,
216        namespace: &mut String,
217    ) -> syn::parse::Result<()> {
218        match tree {
219            UseTree2::Path(input) => {
220                if !namespace.is_empty() {
221                    namespace.push_str("::");
222                }
223
224                namespace.push_str(&input.ident.to_string());
225                self.walk_implement(&input.tree, namespace)?;
226            }
227            UseTree2::Name(_) => {
228                self.implement.push(tree.to_element_type(namespace)?);
229            }
230            UseTree2::Group(input) => {
231                for tree in &input.items {
232                    self.walk_implement(tree, namespace)?;
233                }
234            }
235            UseTree2::TrustLevel(input) => self.trust_level = *input,
236            UseTree2::Agile(agile) => self.agile = *agile,
237        }
238
239        Ok(())
240    }
241}
242
243enum UseTree2 {
244    Path(UsePath2),
245    Name(UseName2),
246    Group(UseGroup2),
247    TrustLevel(usize),
248    Agile(bool),
249}
250
251impl UseTree2 {
252    fn to_element_type(&self, namespace: &mut String) -> syn::parse::Result<ImplementType> {
253        match self {
254            Self::Path(input) => {
255                if !namespace.is_empty() {
256                    namespace.push_str("::");
257                }
258
259                namespace.push_str(&input.ident.to_string());
260                input.tree.to_element_type(namespace)
261            }
262            Self::Name(input) => {
263                let mut type_name = input.ident.to_string();
264                let span = input.ident.span();
265
266                if !namespace.is_empty() {
267                    type_name = format!("{namespace}::{type_name}");
268                }
269
270                let mut generics = vec![];
271
272                for g in &input.generics {
273                    generics.push(g.to_element_type(&mut String::new())?);
274                }
275
276                Ok(ImplementType {
277                    type_name,
278                    generics,
279                    span,
280                })
281            }
282            Self::Group(input) => Err(syn::parse::Error::new(
283                input.brace_token.span.join(),
284                "Syntax not supported",
285            )),
286            _ => unimplemented!(),
287        }
288    }
289}
290
291struct UsePath2 {
292    pub ident: syn::Ident,
293    pub tree: Box<UseTree2>,
294}
295
296struct UseName2 {
297    pub ident: syn::Ident,
298    pub generics: Vec<UseTree2>,
299}
300
301struct UseGroup2 {
302    pub brace_token: syn::token::Brace,
303    pub items: syn::punctuated::Punctuated<UseTree2, syn::Token![,]>,
304}
305
306impl syn::parse::Parse for UseTree2 {
307    fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
308        let lookahead = input.lookahead1();
309        if lookahead.peek(syn::Ident) {
310            use syn::ext::IdentExt;
311            let ident = input.call(syn::Ident::parse_any)?;
312            if input.peek(syn::Token![::]) {
313                input.parse::<syn::Token![::]>()?;
314                Ok(Self::Path(UsePath2 {
315                    ident,
316                    tree: Box::new(input.parse()?),
317                }))
318            } else if input.peek(syn::Token![=]) {
319                if ident == "TrustLevel" {
320                    input.parse::<syn::Token![=]>()?;
321                    let span = input.span();
322                    let value = input.call(syn::Ident::parse_any)?;
323                    match value.to_string().as_str() {
324                        "Partial" => Ok(Self::TrustLevel(1)),
325                        "Full" => Ok(Self::TrustLevel(2)),
326                        _ => Err(syn::parse::Error::new(
327                            span,
328                            "`TrustLevel` must be `Partial` or `Full`",
329                        )),
330                    }
331                } else if ident == "Agile" {
332                    input.parse::<syn::Token![=]>()?;
333                    let span = input.span();
334                    let value = input.call(syn::Ident::parse_any)?;
335                    match value.to_string().as_str() {
336                        "true" => Ok(Self::Agile(true)),
337                        "false" => Ok(Self::Agile(false)),
338                        _ => Err(syn::parse::Error::new(
339                            span,
340                            "`Agile` must be `true` or `false`",
341                        )),
342                    }
343                } else {
344                    Err(syn::parse::Error::new(
345                        ident.span(),
346                        "Unrecognized key-value pair",
347                    ))
348                }
349            } else {
350                let generics = if input.peek(syn::Token![<]) {
351                    input.parse::<syn::Token![<]>()?;
352                    let mut generics = Vec::new();
353                    loop {
354                        generics.push(input.parse::<Self>()?);
355
356                        if input.parse::<syn::Token![,]>().is_err() {
357                            break;
358                        }
359                    }
360                    input.parse::<syn::Token![>]>()?;
361                    generics
362                } else {
363                    Vec::new()
364                };
365
366                Ok(Self::Name(UseName2 { ident, generics }))
367            }
368        } else if lookahead.peek(syn::token::Brace) {
369            let content;
370            let brace_token = syn::braced!(content in input);
371            let items = content.parse_terminated(Self::parse, syn::Token![,])?;
372
373            Ok(Self::Group(UseGroup2 { brace_token, items }))
374        } else {
375            Err(lookahead.error())
376        }
377    }
378}
379
380fn convert_implements_to_interface_chains(implements: Vec<ImplementType>) -> Vec<InterfaceChain> {
381    let mut chains = Vec::with_capacity(implements.len());
382
383    for (i, implement) in implements.into_iter().enumerate() {
384        // Field/const naming uses `i + 1` because interface 0 is the identity interface.
385        let mut ident_string = format!("interface{}", i + 1);
386
387        let suffix = get_interface_ident_suffix(&implement.type_name);
388        if !suffix.is_empty() {
389            ident_string.push('_');
390            ident_string.push_str(&suffix);
391        }
392        let field_ident = syn::Ident::new(&ident_string, implement.span);
393
394        let mut vtable_const_string = ident_string.clone();
395        vtable_const_string.make_ascii_uppercase();
396        vtable_const_string.insert_str(0, "VTABLE_");
397        let vtable_const_ident = syn::Ident::new(&vtable_const_string, implement.span);
398
399        chains.push(InterfaceChain {
400            implement,
401            field_ident,
402            vtable_const_ident,
403        });
404    }
405
406    chains
407}
408
409fn get_interface_ident_suffix(type_name: &str) -> String {
410    let mut suffix = String::new();
411    for c in type_name.chars() {
412        let c = c.to_ascii_lowercase();
413
414        if suffix.len() >= 20 {
415            break;
416        }
417
418        if c.is_ascii_alphanumeric() {
419            suffix.push(c);
420        }
421    }
422
423    suffix
424}