derive_com_impl/
lib.rs

1#![recursion_limit = "1024"]
2
3//! Implements a COM Object struct with automatic reference counting and implements
4//! IUnknown for you. This covers the most common use cases of creating COM objects
5//! from Rust. Supports generic parameters!
6//! 
7//! ```
8//! #[macro_use]
9//! extern crate derive_com_impl;
10//! 
11//! extern crate com_impl;
12//! extern crate winapi;
13//! extern crate wio;
14//! 
15//! use com_impl::{VTable, Refcount};
16//! use winapi::ctypes::c_void;
17//! use winapi::shared::winerror::{ERROR_INVALID_INDEX, HRESULT, HRESULT_FROM_WIN32, S_OK};
18//! use winapi::um::dwrite::{IDWriteFontFileStream, IDWriteFontFileStreamVtbl};
19//! use wio::com::ComPtr;
20//! 
21//! #[repr(C)]
22//! #[derive(ComImpl)]
23//! #[interfaces(IDWriteFontFileStream)]
24//! pub struct FileStream {
25//!     vtbl: VTable<IDWriteFontFileStreamVtbl>,
26//!     refcount: Refcount,
27//!     write_time: u64,
28//!     file_data: Vec<u8>,
29//! }
30//! 
31//! impl FileStream {
32//!     pub fn new(write_time: u64, data: Vec<u8>) -> ComPtr<IDWriteFontFileStream> {
33//!         let ptr = FileStream::create_raw(write_time, data);
34//!         let ptr = ptr as *mut IDWriteFontFileStream;
35//!         unsafe { ComPtr::from_raw(ptr) }
36//!     }
37//! }
38//! 
39//! #[com_impl]
40//! unsafe impl IDWriteFontFileStream for FileStream {
41//!     unsafe fn get_file_size(&self, size: *mut u64) -> HRESULT {
42//!         *size = self.file_data.len() as u64;
43//!         S_OK
44//!     }
45//! 
46//!     unsafe fn get_last_write_time(&self, write_time: *mut u64) -> HRESULT {
47//!         *write_time = self.write_time;
48//!         S_OK
49//!     }
50//! 
51//!     unsafe fn read_file_fragment(
52//!         &self,
53//!         start: *mut *const c_void,
54//!         offset: u64,
55//!         size: u64,
56//!         ctx: *mut *mut c_void,
57//!     ) -> HRESULT {
58//!         if offset > std::isize::MAX as u64 || size > std::isize::MAX as u64 {
59//!             return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);
60//!         }
61//! 
62//!         let offset = offset as usize;
63//!         let size = size as usize;
64//! 
65//!         if offset + size > self.file_data.len() {
66//!             return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);
67//!         }
68//! 
69//!         *start = self.file_data.as_ptr().offset(offset as isize) as *const c_void;
70//!         *ctx = std::ptr::null_mut();
71//! 
72//!         S_OK
73//!     }
74//! 
75//!     unsafe fn release_file_fragment(&self, _ctx: *mut c_void) {
76//!         // Nothing to do
77//!     }
78//! }
79//! 
80//! fn main() {
81//!     let ptr = FileStream::new(100, vec![0xDE, 0xAF, 0x00, 0xF0, 0x01]);
82//! 
83//!     // Do things with ptr
84//! }
85//! ```
86
87#[macro_use]
88extern crate quote;
89#[macro_use]
90extern crate syn;
91
92extern crate proc_macro;
93extern crate proc_macro2;
94
95use proc_macro::TokenStream;
96use syn::DeriveInput;
97use syn::{AttributeArgs, Item};
98
99mod derive;
100mod com_impl;
101
102#[proc_macro_derive(ComImpl, attributes(interfaces))]
103/// `#[derive(ComImpl)]`
104/// 
105/// Automatically implements reference counting for your COM object, creating a pointer via
106/// `Box::into_raw` and deallocating with `Box::from_raw`. A private inherent method named
107/// `create_raw` is added to your type that takes all of your struct members except the vtable
108/// and refcount as parameters in declaration order.
109/// 
110/// ### Additional attributes:
111/// 
112/// `#[interfaces(ISome, IThing)]`
113/// 
114/// - Specifies the COM interfaces that this type should respond to in QueryInterface. IUnknown
115///   is included implicitly. If this attribute is not specified it will be assumed that the only
116///   types responded to are IUnknown and the type specified in the VTable.
117pub fn derive_com_impl(input: TokenStream) -> TokenStream {
118    let input = parse_macro_input!(input as DeriveInput);
119    
120    derive::expand_derive_com_impl(&input)
121        .unwrap_or_else(compile_error)
122        .into()
123}
124
125#[proc_macro_attribute]
126/// `#[com_impl]`
127/// 
128/// Generates a VTable for the functions implemented in the `impl` block this attribute is
129/// applied to.
130/// 
131/// For the general syntax see the example in the crate root.
132/// 
133/// ### Additional parameters
134/// 
135/// `#[com_impl(no_parent)]`
136/// 
137/// Specifies that the vtable being implemented here does not have a `parent` member. These
138/// are very rare, but include IUnknown.
139/// 
140/// ### Attributes on methods
141/// 
142/// `#[com_name = "..."]`
143/// 
144/// Overrides the method name this function corresponds to in the VTable. Method names by
145/// default are mapped from snake_case to PascalCase to determine their winapi names.
146/// 
147/// <hb/>
148/// 
149/// `#[panic(abort)]`
150/// 
151/// Specifies that in the stub function, code should be generated to catch any unwinding from
152/// the user-provided bodies and abort on panic.
153/// 
154/// <hb/>
155/// 
156/// `#[panic(result = "EXPRESSION")]`
157/// 
158/// Specifies that in the stub functions code should be generated to catch any unwinding from
159/// the user-provided bodies and return the specified expression. The expression should have
160/// the same type as the standard function body return. This is most useful with functions that
161/// return an HRESULT.
162pub fn com_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
163    let args = parse_macro_input!(attr as AttributeArgs);
164    let item = parse_macro_input!(item as Item);
165
166    com_impl::expand_com_impl(&args, &item)
167        .unwrap_or_else(compile_error)
168        .into()
169}
170
171fn compile_error(message: String) -> proc_macro2::TokenStream {
172    quote! {
173        compile_error!(#message);
174    }
175}