cglue_macro/lib.rs
1//! CGlue procedural macros
2
3extern crate proc_macro;
4
5use cglue_gen::ext::{ext_abs_remap, prelude_remap_with_ident};
6use cglue_gen::forward::gen_forward;
7use cglue_gen::generics::{GenericCastType, GroupCastType};
8use cglue_gen::trait_groups::*;
9use proc_macro::TokenStream;
10use quote::{format_ident, quote};
11use syn::*;
12
13/// Make a trait CGlue compatible.
14///
15/// This macro will generate vtable structures alongside required traits and implementations needed
16/// for constructing CGlue objects and their groups.
17#[proc_macro_attribute]
18pub fn cglue_trait(_args: TokenStream, input: TokenStream) -> TokenStream {
19 let tr = parse_macro_input!(input as ItemTrait);
20
21 let trait_def = cglue_gen::traits::gen_trait(tr, None);
22
23 trait_def.into()
24}
25
26/// Make an external trait CGlue compatible.
27///
28/// Invoking this macro will change the name of the trait to be prefixed with `Ext`,
29/// and it will act as a wrapper trait for the underlying trait.
30///
31/// This is very useful when third-party crates are needed to be CGlue compatible.
32#[proc_macro_attribute]
33pub fn cglue_trait_ext(_args: TokenStream, input: TokenStream) -> TokenStream {
34 let tr = parse_macro_input!(input as ItemTrait);
35
36 let ext_ident = format_ident!("{}Ext", tr.ident);
37
38 let trait_def = cglue_gen::traits::gen_trait(tr, Some(&ext_ident));
39
40 trait_def.into()
41}
42
43/// Convert into a CGlue compatible object.
44///
45/// The syntax is the same as a cast expression:
46///
47/// ```ignore
48/// trait_obj!(variable as TraitName)
49/// ```
50///
51/// It is possible to pass both owned objects and references.
52#[proc_macro]
53pub fn trait_obj(args: TokenStream) -> TokenStream {
54 let crate_path = cglue_gen::util::crate_path();
55
56 let GenericCastType {
57 mut target,
58 expr,
59 ident,
60 } = parse_macro_input!(args as GenericCastType);
61
62 if let Ok(ident) = parse2::<Ident>(ident) {
63 target.path = ext_abs_remap(prelude_remap_with_ident(target.path, &ident))
64 }
65
66 let gen = quote! {
67 #crate_path::trait_group::Opaquable::into_opaque({
68 // We need rust to infer lifetimes and generics, thus we use a wrapper trait
69 use #crate_path::from2::From2;
70 #target ::from2(#expr)
71 })
72 };
73
74 gen.into()
75}
76
77/// Define a CGlue trait group.
78///
79/// # Arguments
80///
81/// 1. The name of the group.
82///
83/// 2. Mandatory traits for the group. Either a single trait name, or a braced list of traits.
84///
85/// 3. Optionally implemented traits for the group. Either a single trait name, or a braced
86/// list of traits.
87///
88/// 3.1. If the same trait is listed twice (with different generic parameters), it may be aliased
89/// with `OrigTrait<Generic> = TraitAlias`. Then, all subsequent operations, such as `cast!` need
90/// to use the alias, as opposed to the original trait.
91///
92/// 4. Optional block for external trait definitions. This block is needed when using non-standard
93/// external traits.
94#[proc_macro]
95pub fn cglue_trait_group(args: TokenStream) -> TokenStream {
96 let args = parse_macro_input!(args as TraitGroup);
97 args.create_group().into()
98}
99
100/// Implement a CGlue group for a specific type.
101///
102/// # Arguments
103///
104/// 1. The name of the type to implement the group for.
105///
106/// 2. The name of the group to implement.
107///
108/// 3. Optional traits that this object contains. Either a single trait, or a braced list of
109/// traits. Note that the list must redefine identical aliases, as defined in
110/// `cglue_trait_group!` invokation.
111#[proc_macro]
112#[cfg_attr(feature = "unstable", allow(unused))]
113pub fn cglue_impl_group(args: TokenStream) -> TokenStream {
114 #[cfg(not(feature = "unstable"))]
115 {
116 let args = parse_macro_input!(args as TraitGroupImpl);
117 args.implement_group().into()
118 }
119 #[cfg(feature = "unstable")]
120 TokenStream::new()
121}
122
123/// Convert into a CGlue trait group.
124///
125/// The syntax is the same as a cast expression:
126///
127/// ```ignore
128/// group_obj!(variable as GroupName)
129/// ```
130///
131/// It is possible to pass both owned objects and references.
132#[proc_macro]
133pub fn group_obj(args: TokenStream) -> TokenStream {
134 let crate_path = cglue_gen::util::crate_path();
135
136 let GroupCastType {
137 mut target,
138 expr,
139 ident,
140 } = parse_macro_input!(args as GroupCastType);
141
142 if let Ok(ident) = parse2::<Ident>(ident) {
143 target.path = ext_abs_remap(prelude_remap_with_ident(target.path, &ident));
144 }
145
146 let gen = quote! {
147 #crate_path::trait_group::Opaquable::into_opaque({
148 // We need rust to infer lifetimes and generics, thus we use a wrapper trait
149 use #crate_path::from2::From2;
150 #target ::from2(#expr)
151 })
152 };
153
154 gen.into()
155}
156
157/// Checked cast to a list of optional traits.
158///
159/// The syntax is similar to a cast expression, but uses `impl` keyword:
160///
161/// ```ignore
162/// cast!(obj impl Trait1 + Trait2 + Trait3);
163/// ```
164///
165/// `cast!` is non-final, meaning it is possible to cast back to the base group object.
166///
167/// This macro accepts either:
168///
169/// 1. A list of optional traits, without any mandatory traits.
170///
171/// or
172///
173/// 2. A list of optional traits, with every mandatory trait.
174///
175/// In either case a successfully cast object will still implement the mandatory traits.
176#[proc_macro]
177pub fn cast(args: TokenStream) -> TokenStream {
178 let cast = parse_macro_input!(args as TraitCastGroup);
179 cast.cast_group(CastType::Cast).into()
180}
181
182/// Checked cast to a list of optional traits.
183///
184/// The syntax is similar to a cast expression, but uses `impl` keyword:
185///
186/// ```ignore
187/// as_ref!(obj impl Trait1 + Trait2 + Trait3);
188/// ```
189///
190/// `as_ref!` is non-final, meaning once the reference is dropped, the original group object can be
191/// used mutably.
192///
193/// This macro accepts either:
194///
195/// 1. A list of optional traits, without any mandatory traits.
196///
197/// or
198///
199/// 2. A list of optional traits, with every mandatory trait.
200///
201/// In either case a successfully cast object will still implement the mandatory traits.
202#[proc_macro]
203pub fn as_ref(args: TokenStream) -> TokenStream {
204 let cast = parse_macro_input!(args as TraitCastGroup);
205 cast.cast_group(CastType::AsRef).into()
206}
207
208/// Checked cast to a list of optional traits.
209///
210/// The syntax is similar to a cast expression, but uses `impl` keyword:
211///
212/// ```ignore
213/// as_mut!(obj impl Trait1 + Trait2 + Trait3);
214/// ```
215///
216/// `as_mut!` is non-final, meaning once the reference is dropped, the original group object can be
217/// used.
218///
219/// This macro accepts either:
220///
221/// 1. A list of optional traits, without any mandatory traits.
222///
223/// or
224///
225/// 2. A list of optional traits, with every mandatory trait.
226///
227/// In either case a successfully cast object will still implement the mandatory traits.
228#[proc_macro]
229pub fn as_mut(args: TokenStream) -> TokenStream {
230 let cast = parse_macro_input!(args as TraitCastGroup);
231 cast.cast_group(CastType::AsMut).into()
232}
233
234/// Checked cast to a list of optional traits.
235///
236/// The syntax is similar to a cast expression, but uses `impl` keyword:
237///
238/// ```ignore
239/// into!(obj impl Trait1 + Trait2 + Trait3);
240/// ```
241///
242/// `into!` is final. After invoking this conversion it is not possible to retrieve the original
243/// object.
244///
245/// This macro accepts either:
246///
247/// 1. A list of optional traits, without any mandatory traits.
248///
249/// or
250///
251/// 2. A list of optional traits, with every mandatory trait.
252///
253/// In either case a successfully cast object will still implement the mandatory traits.
254#[proc_macro]
255pub fn into(args: TokenStream) -> TokenStream {
256 let cast = parse_macro_input!(args as TraitCastGroup);
257 cast.cast_group(CastType::Into).into()
258}
259
260/// Check if the group can be cast to the specified traits.
261///
262/// The syntax is similar to a cast expression, but uses `impl` keyword:
263///
264/// ```ignore
265/// check!(obj impl Trait1 + Trait2 + Trait3);
266/// ```
267///
268/// The result of `check!` will be a boolean value.
269///
270/// This macro accepts either:
271///
272/// 1. A list of optional traits, without any mandatory traits.
273///
274/// or
275///
276/// 2. A list of optional traits, with every mandatory trait.
277///
278/// In either case a successfully cast object will still implement the mandatory traits.
279#[proc_macro]
280pub fn check(args: TokenStream) -> TokenStream {
281 let cast = parse_macro_input!(args as TraitCastGroup);
282 cast.cast_group(CastType::OnlyCheck).into()
283}
284
285/// Implement builtin external traits.
286#[proc_macro]
287pub fn cglue_builtin_ext_traits(_: TokenStream) -> TokenStream {
288 cglue_gen::ext::impl_store().into()
289}
290
291/// Generate forward trait implementation for Fwd.
292///
293/// This is useful for using references of trait objects as generic parameters.
294#[proc_macro_attribute]
295pub fn cglue_forward(_: TokenStream, input: TokenStream) -> TokenStream {
296 let tr = parse_macro_input!(input as ItemTrait);
297 gen_forward(tr, None).into()
298}
299
300/// Generate forward trait implementation for Fwd.
301///
302/// This is useful for using references of trait objects as generic parameters.
303#[proc_macro_attribute]
304pub fn cglue_forward_ext(args: TokenStream, input: TokenStream) -> TokenStream {
305 let path = parse_macro_input!(args as proc_macro2::TokenStream);
306 let tr = parse_macro_input!(input as ItemTrait);
307 gen_forward(tr, Some(path)).into()
308}
309
310/// Implement [macro@cglue_forward_ext] for all builtin external traits.
311#[proc_macro]
312pub fn cglue_builtin_ext_forward(_: TokenStream) -> TokenStream {
313 cglue_gen::ext::impl_ext_forward().into()
314}
315
316// Marker macros for wrapping
317
318/// Mark the trait or function to use `IntResult`.
319///
320/// This flag has an effect for functions that return `Result<T, E>`, and
321/// is valid when `E` implements `IntResult`. Using this attribute results
322/// in more efficient code generation.
323#[proc_macro_attribute]
324pub fn int_result(_: TokenStream, input: TokenStream) -> TokenStream {
325 input
326}
327
328/// Exclude a single function from using `IntResult`.
329#[proc_macro_attribute]
330pub fn no_int_result(_: TokenStream, input: TokenStream) -> TokenStream {
331 input
332}
333
334/// Skip reimplementing this function.
335#[proc_macro_attribute]
336pub fn skip_func(_: TokenStream, input: TokenStream) -> TokenStream {
337 input
338}
339
340/// Wrap the associated type with a custom type.
341#[proc_macro_attribute]
342pub fn wrap_with(_: TokenStream, input: TokenStream) -> TokenStream {
343 input
344}
345
346/// Specify return type conversion with a closure.
347///
348/// # Arguments
349///
350/// A closure that accepts original return value and outputs the defined type.
351///
352/// If the return type is a reference to the associated type, `ret_tmp` value is available for use
353/// to write the intermediate value into.
354#[proc_macro_attribute]
355pub fn return_wrap(_: TokenStream, input: TokenStream) -> TokenStream {
356 input
357}
358
359/// Wrap the associated type with a CGlue trait object.
360#[proc_macro_attribute]
361pub fn wrap_with_obj(_: TokenStream, input: TokenStream) -> TokenStream {
362 input
363}
364
365/// Wrap the associated type with a CGlue trait object reference.
366#[proc_macro_attribute]
367pub fn wrap_with_obj_ref(_: TokenStream, input: TokenStream) -> TokenStream {
368 input
369}
370
371/// Wrap the associated type with a CGlue trait object mutable reference.
372#[proc_macro_attribute]
373pub fn wrap_with_obj_mut(_: TokenStream, input: TokenStream) -> TokenStream {
374 input
375}
376
377/// Wrap the associated type with a CGlue trait group.
378#[proc_macro_attribute]
379pub fn wrap_with_group(_: TokenStream, input: TokenStream) -> TokenStream {
380 input
381}
382
383/// Wrap the associated type with a CGlue trait group reference.
384///
385/// # SAFETY WARNING
386///
387///
388#[proc_macro_attribute]
389pub fn wrap_with_group_ref(_: TokenStream, input: TokenStream) -> TokenStream {
390 input
391}
392
393/// Wrap the associated type with a CGlue trait group mutable reference.
394#[proc_macro_attribute]
395pub fn wrap_with_group_mut(_: TokenStream, input: TokenStream) -> TokenStream {
396 input
397}
398
399/// Add custom wrapping for a trait impl and the C interface.
400///
401/// This is a pretty complex, and fine-grained operation. It allows to control almost every aspect
402/// of type wrapping. The logic of argument layout is a declaration and sequence of actions
403/// from top to bottom.
404///
405/// In addition, this is the only way to allow using generic parameters within functions - C
406/// implementation must have them converted to concrete types beforehand.
407///
408/// Example from [cglue-gen](cglue_gen::ext::core::fmt).
409///
410/// ```ignore
411/// #[custom_impl(
412/// // Types within the C interface other than self and additional wrappers.
413/// {
414/// f_out: &mut WriteMut,
415/// },
416/// // Unwrapped return type
417/// Result<(), ::core::fmt::Error>,
418/// // Conversion in trait impl to C arguments (signature names are expected).
419/// {
420/// let f_out: WriteBaseMut<::core::fmt::Formatter> = From::from(f);
421/// let f_out = &mut #crate_path::trait_group::Opaquable::into_opaque(f_out);
422/// },
423/// // This is the body of C impl minus the automatic wrapping.
424/// {
425/// write!(f_out, #fmt_str, this)
426/// },
427/// // This part is processed in the trait impl after the call returns (impl_func_ret,
428/// // nothing extra needs to happen here).
429/// {
430/// },
431/// )]
432/// ```
433#[proc_macro_attribute]
434pub fn custom_impl(_: TokenStream, input: TokenStream) -> TokenStream {
435 input
436}
437
438/// Emit a vtable entry, but do not use it in Rust.
439///
440/// This allows to expose functionality to C/C++ users with slight changes in return types,
441/// while making use of the blanket implementation in Rust. It is necessary when a function
442/// itself wraps `Self` into some specific type, and would otherwise be completely
443/// incompatible with `CGlue`.
444///
445/// User is able to specify one of the wrapping macros to configure the behavior:
446///
447/// ```ignore
448/// #[vtbl_only(wrap_with_obj(ExampleTrait))]
449/// ```
450///
451/// Note that there could be some parity issues between Rust and C/C++ APIs, because in Rust the
452/// blanket implementation will be invoked, while in FFI the underlying implementation will be
453/// called.
454#[proc_macro_attribute]
455pub fn vtbl_only(_: TokenStream, input: TokenStream) -> TokenStream {
456 input
457}