1use quote::quote;
8use syn::spanned::Spanned;
9
10#[proc_macro_attribute]
35pub fn interface(
36 attributes: proc_macro::TokenStream,
37 original_type: proc_macro::TokenStream,
38) -> proc_macro::TokenStream {
39 let guid = syn::parse_macro_input!(attributes as Guid);
40 let interface = syn::parse_macro_input!(original_type as Interface);
41 let tokens = match interface.gen_tokens(&guid) {
42 Ok(t) => t,
43 Err(e) => return e.to_compile_error().into(),
44 };
45 tokens.into()
46}
47
48macro_rules! bail {
49 ($item:expr, $($msg:tt),*) => {
50 return Err(syn::Error::new($item.span(), std::fmt::format(format_args!($($msg),*))));
51 };
52
53}
54
55macro_rules! unexpected_token {
56 ($item:expr, $msg:expr) => {
57 if let Some(i) = $item {
58 bail!(i, "unexpected {}", $msg);
59 }
60 };
61}
62macro_rules! expected_token {
63 ($sig:tt.$item:tt(), $msg:expr) => {
64 if let None = $sig.$item() {
65 bail!($sig, "expected {}", $msg);
66 }
67 };
68}
69
70struct Interface {
80 visibility: syn::Visibility,
81 name: syn::Ident,
82 parent: Option<syn::Path>,
83 methods: Vec<InterfaceMethod>,
84 docs: Vec<syn::Attribute>,
85}
86
87impl Interface {
88 fn gen_tokens(&self, guid: &Guid) -> syn::Result<proc_macro2::TokenStream> {
90 let vis = &self.visibility;
91 let name = &self.name;
92 let docs = &self.docs;
93 let parent = self.parent_type();
94 let vtable_name = quote::format_ident!("{}_Vtbl", name);
95 let guid = guid.to_tokens()?;
96 let implementation = self.gen_implementation();
97 let com_trait = self.get_com_trait();
98 let vtable = self.gen_vtable(&vtable_name);
99 let conversions = self.gen_conversions();
100
101 Ok(quote! {
102 #[repr(transparent)]
103 #(#docs)*
104 #vis struct #name(#parent);
105 #implementation
106 unsafe impl ::windows_core::Interface for #name {
107 type Vtable = #vtable_name;
108 const IID: ::windows_core::GUID = #guid;
109 }
110 impl ::windows_core::RuntimeName for #name {}
111 impl ::core::ops::Deref for #name {
112 type Target = #parent;
113 fn deref(&self) -> &Self::Target {
114 unsafe { ::core::mem::transmute(self) }
115 }
116 }
117 #com_trait
118 #vtable
119 #conversions
120 })
121 }
122
123 fn gen_implementation(&self) -> proc_macro2::TokenStream {
125 let name = &self.name;
126 let methods = self
127 .methods
128 .iter()
129 .map(|m| {
130 let vis = &m.visibility;
131 let name = &m.name;
132
133 let generics = m.gen_consume_generics();
134 let params = m.gen_consume_params();
135 let args = m.gen_consume_args();
136 let ret = &m.ret;
137
138 if m.is_result() {
139 quote! {
140 #[inline(always)]
141 #vis unsafe fn #name<#(#generics),*>(&self, #(#params),*) #ret {
142 (::windows_core::Interface::vtable(self).#name)(::windows_core::Interface::as_raw(self), #(#args),*).ok()
143 }
144 }
145 } else {
146 quote! {
147 #[inline(always)]
148 #vis unsafe fn #name<#(#generics),*>(&self, #(#params),*) #ret {
149 (::windows_core::Interface::vtable(self).#name)(::windows_core::Interface::as_raw(self), #(#args),*)
150 }
151 }
152 }
153 })
154 .collect::<Vec<_>>();
155 quote! {
156 impl #name {
157 #(#methods)*
158 }
159 }
160 }
161
162 fn get_com_trait(&self) -> proc_macro2::TokenStream {
163 let name = quote::format_ident!("{}_Impl", self.name);
164 let vis = &self.visibility;
165 let methods = self
166 .methods
167 .iter()
168 .map(|m| {
169 let name = &m.name;
170 let docs = &m.docs;
171 let args = m.gen_args();
172 let ret = &m.ret;
173 quote! {
174 #(#docs)*
175 unsafe fn #name(&self, #(#args),*) #ret;
176 }
177 })
178 .collect::<Vec<_>>();
179 let parent = self.parent_trait_constraint();
180
181 quote! {
182 #[allow(non_camel_case_types)]
183 #vis trait #name: Sized + #parent {
184 #(#methods)*
185 }
186 }
187 }
188
189 fn gen_vtable(&self, vtable_name: &syn::Ident) -> proc_macro2::TokenStream {
191 let vis = &self.visibility;
192 let name = &self.name;
193 let trait_name = quote::format_ident!("{}_Impl", name);
194 let implvtbl_name = quote::format_ident!("{}_ImplVtbl", name);
195
196 let vtable_entries = self
197 .methods
198 .iter()
199 .map(|m| {
200 let name = &m.name;
201 let ret = &m.ret;
202 let args = m.gen_args();
203
204 if m.is_result() {
205 quote! {
206 pub #name: unsafe extern "system" fn(this: *mut ::core::ffi::c_void, #(#args),*) -> ::windows_core::HRESULT,
207 }
208 } else {
209 quote! {
210 pub #name: unsafe extern "system" fn(this: *mut ::core::ffi::c_void, #(#args),*) #ret,
211 }
212 }
213 })
214 .collect::<Vec<_>>();
215
216 let parent_vtable_generics = quote!(Identity, OFFSET);
217 let parent_vtable = self.parent_vtable();
218
219 let or_parent_matches = match parent_vtable.as_ref() {
226 Some(parent) if !self.parent_is_iunknown() => quote! (|| <#parent>::matches(iid)),
227 _ => quote!(),
228 };
229
230 let functions = self
231 .methods
232 .iter()
233 .map(|m| {
234 let name = &m.name;
235 let args = m.gen_args();
236 let params = &m
237 .args
238 .iter()
239 .map(|a| {
240 let pat = &a.pat;
241 quote! { #pat }
242 })
243 .collect::<Vec<_>>();
244 let ret = &m.ret;
245
246 let ret = if m.is_result() {
247 quote! { -> ::windows_core::HRESULT }
248 } else {
249 quote! { #ret }
250 };
251
252 if parent_vtable.is_some() {
253 quote! {
254 unsafe extern "system" fn #name<
255 Identity: ::windows_core::IUnknownImpl,
256 const OFFSET: isize
257 >(
258 this: *mut ::core::ffi::c_void, #(#args),*
260 ) #ret
261 where
262 Identity : #trait_name
263 {
264 let this_outer: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity);
270
271 <Identity as #trait_name>::#name(this_outer, #(#params),*).into()
276 }
277 }
278 } else {
279 quote! {
280 unsafe extern "system" fn #name<Impl: #trait_name>(this: *mut ::core::ffi::c_void, #(#args),*) #ret {
281 let this = (this as *mut *mut ::core::ffi::c_void) as *const ::windows_core::ScopedHeap;
282 let this = (*this).this as *const Impl;
283 (*this).#name(#(#params),*).into()
284 }
285 }
286 }
287 })
288 .collect::<Vec<_>>();
289
290 if let Some(parent_vtable) = parent_vtable {
291 let entries = self
292 .methods
293 .iter()
294 .map(|m| {
295 let name = &m.name;
296 quote!(#name: #name::<Identity, OFFSET>)
297 })
298 .collect::<Vec<_>>();
299
300 quote! {
301 #[repr(C)]
302 #[doc(hidden)]
303 #vis struct #vtable_name {
304 pub base__: #parent_vtable,
305 #(#vtable_entries)*
306 }
307 impl #vtable_name {
308 pub const fn new<
309 Identity: ::windows_core::IUnknownImpl,
310 const OFFSET: isize,
311 >() -> Self
312 where
313 Identity : #trait_name
314 {
315 #(#functions)*
316 Self { base__: #parent_vtable::new::<#parent_vtable_generics>(), #(#entries),* }
317 }
318
319 #[inline(always)]
320 pub fn matches(iid: &::windows_core::GUID) -> bool {
321 *iid == <#name as ::windows_core::Interface>::IID
322 #or_parent_matches
323 }
324 }
325 }
326 } else {
327 let entries = self
328 .methods
329 .iter()
330 .map(|m| {
331 let name = &m.name;
332 quote!(#name: #name::<Impl>)
333 })
334 .collect::<Vec<_>>();
335
336 quote! {
337 #[repr(C)]
338 #[doc(hidden)]
339 #vis struct #vtable_name {
340 #(#vtable_entries)*
341 }
342 impl #vtable_name {
343 pub const fn new<Impl: #trait_name>() -> Self {
344 #(#functions)*
345 Self { #(#entries),* }
346 }
347 }
348 struct #implvtbl_name<T: #trait_name> (::core::marker::PhantomData<T>);
349 impl<T: #trait_name> #implvtbl_name<T> {
350 const VTABLE: #vtable_name = #vtable_name::new::<T>();
351 }
352 impl #name {
353 fn new<'a, T: #trait_name>(this: &'a T) -> ::windows_core::ScopedInterface<'a, #name> {
354 let this = ::windows_core::ScopedHeap { vtable: &#implvtbl_name::<T>::VTABLE as *const _ as *const _, this: this as *const _ as *const _ };
355 let this = ::core::mem::ManuallyDrop::new(::windows_core::imp::Box::new(this));
356 unsafe { ::windows_core::ScopedInterface::new(::core::mem::transmute(&this.vtable)) }
357 }
358 }
359 }
360 }
361 }
362
363 fn gen_conversions(&self) -> proc_macro2::TokenStream {
365 let name = &self.name;
366 let name_string = format!("{name}");
367 quote! {
368 impl ::core::convert::From<#name> for ::windows_core::IUnknown {
369 fn from(value: #name) -> Self {
370 unsafe { ::core::mem::transmute(value) }
371 }
372 }
373 impl ::core::convert::From<&#name> for ::windows_core::IUnknown {
374 fn from(value: &#name) -> Self {
375 ::core::convert::From::from(::core::clone::Clone::clone(value))
376 }
377 }
378 impl ::core::clone::Clone for #name {
379 fn clone(&self) -> Self {
380 Self(self.0.clone())
381 }
382 }
383 impl ::core::cmp::PartialEq for #name {
384 fn eq(&self, other: &Self) -> bool {
385 self.0 == other.0
386 }
387 }
388 impl ::core::cmp::Eq for #name {}
389 impl ::core::fmt::Debug for #name {
390 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
391 f.debug_tuple(#name_string).field(&::windows_core::Interface::as_raw(self)).finish()
392 }
393 }
394 }
395 }
396
397 fn parent_type(&self) -> proc_macro2::TokenStream {
398 if let Some(parent) = &self.parent {
399 quote!(#parent)
400 } else {
401 quote!(::core::ptr::NonNull<::core::ffi::c_void>)
402 }
403 }
404
405 fn parent_vtable(&self) -> Option<proc_macro2::TokenStream> {
406 if let Some((ident, path)) = self.parent_path().split_last() {
407 let ident = quote::format_ident!("{}_Vtbl", ident);
408 Some(quote! { #(#path::)* #ident })
409 } else {
410 None
411 }
412 }
413
414 fn parent_is_iunknown(&self) -> bool {
415 if let Some(ident) = self.parent_path().last() {
416 ident == "IUnknown"
417 } else {
418 false
419 }
420 }
421
422 fn parent_path(&self) -> Vec<syn::Ident> {
423 if let Some(parent) = &self.parent {
424 parent
425 .segments
426 .iter()
427 .map(|segment| segment.ident.clone())
428 .collect()
429 } else {
430 vec![]
431 }
432 }
433
434 fn parent_trait_constraint(&self) -> proc_macro2::TokenStream {
436 if let Some((ident, path)) = self.parent_path().split_last() {
437 if ident != "IUnknown" {
438 let ident = quote::format_ident!("{}_Impl", ident);
439 return quote! { #(#path::)* #ident };
440 }
441 }
442
443 quote! {}
444 }
445}
446
447impl syn::parse::Parse for Interface {
448 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
449 let attributes = input.call(syn::Attribute::parse_outer)?;
450 let mut docs = Vec::new();
451 for attr in attributes.into_iter() {
452 let path = attr.path();
453 if path.is_ident("doc") {
454 docs.push(attr);
455 } else {
456 return Err(syn::Error::new(path.span(), "Unrecognized attribute "));
457 }
458 }
459
460 let visibility = input.parse::<syn::Visibility>()?;
461 _ = input.parse::<syn::Token![unsafe]>()?;
462 _ = input.parse::<syn::Token![trait]>()?;
463 let name = input.parse::<syn::Ident>()?;
464 _ = input.parse::<syn::Token![:]>();
465 let parent = input.parse::<syn::Path>().ok();
466 let content;
467 syn::braced!(content in input);
468 let mut methods = Vec::new();
469 while !content.is_empty() {
470 methods.push(content.parse::<InterfaceMethod>()?);
471 }
472 Ok(Self {
473 visibility,
474 methods,
475 name,
476 parent,
477 docs,
478 })
479 }
480}
481
482struct Guid(Option<syn::LitStr>);
492
493impl Guid {
494 fn to_tokens(&self) -> syn::Result<proc_macro2::TokenStream> {
495 fn hex_lit(num: &str) -> syn::LitInt {
496 syn::LitInt::new(&format!("0x{num}"), proc_macro2::Span::call_site())
497 }
498
499 fn ensure_length(
500 part: Option<&str>,
501 index: usize,
502 length: usize,
503 span: proc_macro2::Span,
504 ) -> syn::Result<String> {
505 let part = match part {
506 Some(p) => p,
507 None => {
508 return Err(syn::Error::new(
509 span,
510 format!("The IID missing part at index {index}"),
511 ))
512 }
513 };
514
515 if part.len() != length {
516 return Err(syn::Error::new(
517 span,
518 format!(
519 "The IID part at index {} must be {} characters long but was {} characters",
520 index,
521 length,
522 part.len()
523 ),
524 ));
525 }
526
527 Ok(part.to_owned())
528 }
529
530 if let Some(value) = &self.0 {
531 let guid_value = value.value();
532 let mut delimited = guid_value.split('-').fuse();
533 let chunks = [
534 ensure_length(delimited.next(), 0, 8, value.span())?,
535 ensure_length(delimited.next(), 1, 4, value.span())?,
536 ensure_length(delimited.next(), 2, 4, value.span())?,
537 ensure_length(delimited.next(), 3, 4, value.span())?,
538 ensure_length(delimited.next(), 4, 12, value.span())?,
539 ];
540
541 let data1 = hex_lit(&chunks[0]);
542 let data2 = hex_lit(&chunks[1]);
543 let data3 = hex_lit(&chunks[2]);
544 let (data4_1, data4_2) = chunks[3].split_at(2);
545 let data4_1 = hex_lit(data4_1);
546 let data4_2 = hex_lit(data4_2);
547 let (data4_3, rest) = chunks[4].split_at(2);
548 let data4_3 = hex_lit(data4_3);
549
550 let (data4_4, rest) = rest.split_at(2);
551 let data4_4 = hex_lit(data4_4);
552
553 let (data4_5, rest) = rest.split_at(2);
554 let data4_5 = hex_lit(data4_5);
555
556 let (data4_6, rest) = rest.split_at(2);
557 let data4_6 = hex_lit(data4_6);
558
559 let (data4_7, data4_8) = rest.split_at(2);
560 let data4_7 = hex_lit(data4_7);
561 let data4_8 = hex_lit(data4_8);
562 Ok(quote! {
563 ::windows_core::GUID {
564 data1: #data1,
565 data2: #data2,
566 data3: #data3,
567 data4: [#data4_1, #data4_2, #data4_3, #data4_4, #data4_5, #data4_6, #data4_7, #data4_8]
568 }
569 })
570 } else {
571 Ok(quote! {
572 ::windows_core::GUID::zeroed()
573 })
574 }
575 }
576}
577
578impl syn::parse::Parse for Guid {
579 fn parse(cursor: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
580 let string: Option<syn::LitStr> = cursor.parse().ok();
581
582 Ok(Self(string))
583 }
584}
585
586struct InterfaceMethod {
596 pub name: syn::Ident,
597 pub visibility: syn::Visibility,
598 pub args: Vec<InterfaceMethodArg>,
599 pub ret: syn::ReturnType,
600 pub docs: Vec<syn::Attribute>,
601}
602
603impl InterfaceMethod {
604 fn is_result(&self) -> bool {
605 if let syn::ReturnType::Type(_, ty) = &self.ret {
606 if let syn::Type::Path(path) = &**ty {
607 if let Some(segment) = path.path.segments.last() {
608 let ident = segment.ident.to_string();
609 if ident == "Result" {
610 if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
611 if args.args.len() == 1 {
612 return true;
613 }
614 }
615 }
616 }
617 }
618 }
619
620 false
621 }
622
623 fn gen_args(&self) -> Vec<proc_macro2::TokenStream> {
625 self.args
626 .iter()
627 .map(|a| {
628 let pat = &a.pat;
629 let ty = &a.ty;
630 quote! { #pat: #ty }
631 })
632 .collect::<Vec<_>>()
633 }
634
635 fn gen_consume_generics(&self) -> Vec<proc_macro2::TokenStream> {
636 self.args
637 .iter()
638 .enumerate()
639 .filter_map(|(generic_index, a)| {
640 if let Some((ty, ident)) = a.borrow_type() {
641 let generic_ident = quote::format_ident!("P{generic_index}");
642 if ident == "Ref" {
643 Some(quote! { #generic_ident: ::windows_core::Param<#ty> })
644 } else {
645 Some(quote! { #generic_ident: ::windows_core::OutParam<#ty> })
646 }
647 } else {
648 None
649 }
650 })
651 .collect::<Vec<_>>()
652 }
653
654 fn gen_consume_params(&self) -> Vec<proc_macro2::TokenStream> {
655 self.args
656 .iter()
657 .enumerate()
658 .map(|(generic_index, a)| {
659 let pat = &a.pat;
660
661 if a.borrow_type().is_some() {
662 let generic_ident = quote::format_ident!("P{generic_index}");
663 quote! { #pat: #generic_ident }
664 } else {
665 let ty = &a.ty;
666 quote! { #pat: #ty }
667 }
668 })
669 .collect::<Vec<_>>()
670 }
671
672 fn gen_consume_args(&self) -> Vec<proc_macro2::TokenStream> {
673 self.args
674 .iter()
675 .map(|a| {
676 let pat = &a.pat;
677
678 if let Some((_, ident)) = a.borrow_type() {
679 if ident == "Ref" {
680 quote! { #pat.param().borrow() }
681 } else {
682 quote! { #pat.borrow_mut() }
683 }
684 } else {
685 quote! { #pat }
686 }
687 })
688 .collect::<Vec<_>>()
689 }
690}
691
692impl syn::parse::Parse for InterfaceMethod {
693 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
694 let docs = input.call(syn::Attribute::parse_outer)?;
695 let visibility = input.parse::<syn::Visibility>()?;
696 let method = input.parse::<syn::TraitItemFn>()?;
697 unexpected_token!(docs.iter().find(|a| !a.path().is_ident("doc")), "attribute");
698 unexpected_token!(method.default, "default method implementation");
699 let sig = method.sig;
700 unexpected_token!(sig.abi, "abi declaration");
701 unexpected_token!(sig.asyncness, "async declaration");
702 unexpected_token!(sig.generics.params.iter().next(), "generics declaration");
703 unexpected_token!(sig.constness, "const declaration");
704 expected_token!(
705 sig.receiver(),
706 "the method to have &self as its first argument"
707 );
708 unexpected_token!(sig.variadic, "variadic args");
709 let args = sig
710 .inputs
711 .into_iter()
712 .filter_map(|a| match a {
713 syn::FnArg::Receiver(_) => None,
714 syn::FnArg::Typed(p) => Some(p),
715 })
716 .map(|p| {
717 Ok(InterfaceMethodArg {
718 ty: p.ty,
719 pat: p.pat,
720 })
721 })
722 .collect::<Result<Vec<InterfaceMethodArg>, syn::Error>>()?;
723
724 let ret = sig.output;
725 Ok(InterfaceMethod {
726 name: sig.ident,
727 visibility,
728 args,
729 ret,
730 docs,
731 })
732 }
733}
734
735struct InterfaceMethodArg {
737 pub ty: Box<syn::Type>,
739 pub pat: Box<syn::Pat>,
741}
742
743impl InterfaceMethodArg {
744 fn borrow_type(&self) -> Option<(syn::Type, String)> {
745 if let syn::Type::Path(path) = &*self.ty {
746 if let Some(segment) = path.path.segments.last() {
747 let ident = segment.ident.to_string();
748 if matches!(ident.as_str(), "Ref" | "OutRef") {
749 if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
750 if args.args.len() == 1 {
751 if let Some(syn::GenericArgument::Type(ty)) = args.args.first() {
752 return Some((ty.clone(), ident));
753 }
754 }
755 }
756 }
757 }
758 }
759
760 None
761 }
762}